initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
use Database\Seeders\AdminUserSeeder;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 100);
|
||||
$table->string('email', 100)->unique();
|
||||
$table->string('password');
|
||||
$table->string('institution', 100)->nullable();
|
||||
$table->enum('gender', ['male', 'female', 'prefer not to say'])->default('prefer not to say')->nullable();
|
||||
$table->string('phone_number', 50)->nullable();
|
||||
$table->text('tujuan_permohonan')->nullable();
|
||||
$table->enum('role', ['admin', 'operator', 'peneliti'])->default('peneliti');
|
||||
$table->enum('approval_status', ['pending', 'approved', 'rejected'])->default('pending');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
(new AdminUserSeeder())->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->string('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('api_keys', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('api_keys');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('diseases', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->text('deskripsi');
|
||||
$table->enum('visibilitas', ['publik', 'privat'])->default('publik');
|
||||
$table->jsonb('schema');
|
||||
$table->string('cover_page')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('diseases');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('disease_operator', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('disease_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('disease_operator');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('disease_records', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('disease_id')->constrained()->onDelete('cascade');
|
||||
$table->jsonb('data');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('disease_records');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('is_primary_admin')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('is_primary_admin');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('action');
|
||||
$table->string('model')->nullable();
|
||||
$table->unsignedBigInteger('model_id')->nullable();
|
||||
$table->text('changes')->nullable();
|
||||
$table->string('description')->nullable();
|
||||
$table->timestamp('timestamp')->useCurrent();
|
||||
$table->boolean('is_success');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('logs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateCommentsTable extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('disease_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->text('content')->nullable(); // Allow null for deleted comments
|
||||
$table->foreignId('parent_id')->nullable()->constrained('comments')->nullOnDelete();
|
||||
$table->enum('status', ['active', 'deleted'])->default('active'); // Add status column
|
||||
$table->softDeletes(); // Add soft deletes
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('comments');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class AddPrimaryAdmin extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$user = User::where('email', 'admin@example.com')->first();
|
||||
if ($user && !$user->is_primary_admin) {
|
||||
$user->is_primary_admin = true;
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class AdminUserSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if (!User::where('email', 'admin@example.com')->exists()) {
|
||||
$user = User::create([
|
||||
'name' => 'Admin User',
|
||||
'email' => env('ADMIN_DEFAULT_EMAIL', 'admin@example.com'),
|
||||
'password' => Hash::make(env('ADMIN_DEFAULT_PASSWORD', 'password')),
|
||||
'role' => 'admin',
|
||||
'approval_status' => 'approved',
|
||||
]);
|
||||
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// \App\Models\User::factory(10)->create();
|
||||
|
||||
// \App\Models\User::factory()->create([
|
||||
// 'name' => 'Test User',
|
||||
// 'email' => 'test@example.com',
|
||||
// ]);
|
||||
|
||||
$this->call([
|
||||
//AdminUserSeeder::class,
|
||||
DiseaseSeeder::class,
|
||||
DiseaseRecordSeeder::class,
|
||||
AddPrimaryAdmin::class,
|
||||
// other seeders here
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\Disease;
|
||||
use App\Models\DiseaseRecord;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class DiseaseRecordSeeder extends Seeder
|
||||
{
|
||||
private $sampleFiles = [
|
||||
'example1.wav' => ['path' => 'diseases/samples/example1.wav', 'mime' => 'audio/wav'],
|
||||
'example2.wav' => ['path' => 'diseases/samples/example2.wav', 'mime' => 'audio/wav'],
|
||||
];
|
||||
|
||||
public function run()
|
||||
{
|
||||
// Create the diseases/records directory if it doesn't exist
|
||||
Storage::makeDirectory('diseases/records');
|
||||
|
||||
$this->storeSampleFiles();
|
||||
|
||||
$diseases = Disease::all();
|
||||
|
||||
foreach ($diseases as $disease) {
|
||||
$records = [];
|
||||
|
||||
// Loop to create records for the disease
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$recordData = $this->generateRecordData($disease->name, $i, $disease->id);
|
||||
|
||||
$records[] = [
|
||||
'disease_id' => $disease->id,
|
||||
'data' => json_encode($recordData),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
// Insert all disease records in batch
|
||||
DiseaseRecord::insert($records);
|
||||
}
|
||||
}
|
||||
|
||||
private function generateRecordData($diseaseName, $index, $diseaseId)
|
||||
{
|
||||
$basePath = "diseases/records/$diseaseId";
|
||||
|
||||
// Use the same file name for all records
|
||||
$commonFile1 = 'example1.wav'; // This is the same for all records
|
||||
$commonFile2 = 'example2.wav'; // This is the same for all records
|
||||
|
||||
return match($diseaseName) {
|
||||
'Arrhythmia' => [
|
||||
'record' => 'Record_' . $index,
|
||||
'annotations' => 'Annotation_' . $index,
|
||||
'jenis' => collect(['Type1', 'Type2'])->random(),
|
||||
'signals' => 'Signal_' . $index,
|
||||
'durasi' => rand(5, 20) + (rand(0, 99) / 100),
|
||||
'tanggal_tes' => Carbon::now()->subDays(rand(0, 365))->toDateString(),
|
||||
'file_detak_jantung' => "$basePath/$commonFile1", // Same file for all records
|
||||
],
|
||||
'Myocardial' => [
|
||||
'nama_pasien' => 'Patient_' . $index,
|
||||
'umur' => rand(30, 70),
|
||||
'jenis_kelamin' => collect(['Male', 'Female'])->random(),
|
||||
'tanggal_lahir' => Carbon::now()->subYears(rand(30, 70))->toDateString(),
|
||||
'tempat_tes' => 'Hospital_' . rand(1, 10),
|
||||
'tanggal_tes' => Carbon::now()->subDays(rand(0, 365))->toDateString(),
|
||||
'record_data' => ["$basePath/$commonFile1", "$basePath/$commonFile2"], // Same file for all records
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private function storeSampleFiles()
|
||||
{
|
||||
// Manually assign the file paths to different disease IDs
|
||||
$diseaseRecords = [
|
||||
1 => ['example1.wav'], // Store example1.wav for disease_id 1
|
||||
2 => ['example1.wav', 'example2.wav'], // Store both example1.wav and example2.wav for disease_id 2
|
||||
];
|
||||
|
||||
foreach ($diseaseRecords as $diseaseId => $files) {
|
||||
$basePath = "public/diseases/records/$diseaseId"; // Dynamic path based on disease_id
|
||||
Storage::makeDirectory($basePath); // Ensure the disease-specific record directory exists
|
||||
|
||||
foreach ($files as $filename) {
|
||||
$sourcePath = database_path('seeders/samples/' . $filename);
|
||||
|
||||
if (file_exists($sourcePath)) {
|
||||
// Store the file under the disease's records folder
|
||||
Storage::put("$basePath/$filename", file_get_contents($sourcePath));
|
||||
} else {
|
||||
// Handle case where file doesn't exist (use placeholder)
|
||||
Storage::put("$basePath/$filename", 'Sample file content for ' . $filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\Disease;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class DiseaseSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
Storage::makeDirectory('public/diseases/covers');
|
||||
|
||||
$diseases = [
|
||||
[
|
||||
'name' => 'Arrhythmia',
|
||||
'deskripsi' => 'A condition where the heart beats with an irregular or abnormal rhythm.',
|
||||
'visibilitas' => 'publik',
|
||||
'schema' => [
|
||||
"columns" => [
|
||||
["name" => "record", "type" => "string", "is_visible" => true],
|
||||
["name" => "annotations", "type" => "string", "is_visible" => true],
|
||||
["name" => "jenis", "type" => "string", /*"options" => ["Type1", "Type2"]*/ "is_visible" => true],
|
||||
["name" => "signals", "type" => "string", "is_visible" => true],
|
||||
["name" => "durasi", "type" => "decimal", "is_visible" => true],
|
||||
["name" => "tanggal_tes", "type" => "datetime", "is_visible" => true],
|
||||
["name" => "file_detak_jantung", "type" => "file", "format" => "audio"]
|
||||
]
|
||||
],
|
||||
'cover_page' => null,
|
||||
],
|
||||
[
|
||||
'name' => 'Myocardial',
|
||||
'deskripsi' => 'A condition related to heart muscle injury or damage.',
|
||||
'visibilitas' => 'publik',
|
||||
'schema' => [
|
||||
"columns" => [
|
||||
["name" => "nama_pasien", "type" => "string", "is_visible" => true],
|
||||
["name" => "umur", "type" => "integer", "is_visible" => true],
|
||||
["name" => "jenis_kelamin", "type" => "string", /*"options" => ["Male", "Female"]*/ "is_visible" => true],
|
||||
["name" => "tanggal_lahir", "type" => "date", "is_visible" => true],
|
||||
["name" => "tempat_tes", "type" => "string", "is_visible" => true],
|
||||
["name" => "tanggal_tes", "type" => "datetime", "is_visible" => true],
|
||||
["name" => "record_data", "type" => "file", "format" => "audio", "multiple" => 1]
|
||||
]
|
||||
],
|
||||
'cover_page' => null,
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($diseases as $disease) {
|
||||
$createdDisease = Disease::create($disease);
|
||||
|
||||
$imageName = Str::slug($disease['name']) . '.jpg';
|
||||
$sourcePath = database_path('seeders/images/' . $imageName);
|
||||
|
||||
if (file_exists($sourcePath)) {
|
||||
$destinationPath = "diseases/covers/disease-{$createdDisease->id}-cover.jpg";
|
||||
Storage::put('public/' . $destinationPath, file_get_contents($sourcePath));
|
||||
$createdDisease->update(['cover_page' => $destinationPath]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 847 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 552 KiB |
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user