final chapter after sidang

This commit is contained in:
vionar3 2025-07-17 14:26:59 +07:00
parent d592d6ea43
commit ce7d38fc76
12 changed files with 364 additions and 141 deletions

View File

@ -13,6 +13,7 @@
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use App\Helpers\ResponseFormatter; use App\Helpers\ResponseFormatter;
use Illuminate\Support\Facades\DB;
class ProgressController extends Controller class ProgressController extends Controller
{ {
@ -139,150 +140,218 @@ public function getProgressPercentageById(Request $request, $user_id)
// Ambil semua submateri yang ada di sistem // Ambil semua submateri yang ada di sistem
$totalSubmateri = SubMateri::count(); // Menghitung semua submateri yang ada $totalSubmateri = SubMateri::count(); // Menghitung semua submateri yang ada
$completedSubmateri = Progress::where('user_id', $user->id)
->where('status', 'selesai') // Ambil progres terbaru untuk setiap submateri berdasarkan user_id dan submateri_id
->count(); // Menghitung submateri yang statusnya selesai $completedSubmateriQuery = Progress::where('user_id', $user->id)
->where('status', 'selesai') // Hanya yang selesai
->orderBy('updated_at', 'desc') // Mengurutkan berdasarkan updated_at
->get();
// Filter hanya progres terbaru untuk setiap submateri
$completedSubmateri = $completedSubmateriQuery->unique('sub_materi_id');
// Menghitung jumlah submateri yang telah selesai (terbaru)
$completedCount = $completedSubmateri->count();
// Menghitung persentase progres // Menghitung persentase progres
$progressPercentage = ($totalSubmateri > 0) ? ($completedSubmateri / $totalSubmateri) * 100 : 0; $progressPercentage = ($totalSubmateri > 0) ? ($completedCount / $totalSubmateri) * 100 : 0;
// Return hasil persentase progres // Return hasil persentase progres
return response()->json([ return response()->json([
'user_id' => $user->id, 'user_id' => $user->id,
'nama_lengkap' => $user->nama_lengkap, 'nama_lengkap' => $user->nama_lengkap,
'total_submateri' => $totalSubmateri, 'total_submateri' => $totalSubmateri,
'completed_submateri' => $completedSubmateri, 'completed_submateri' => $completedCount,
'progress_percentage' => $progressPercentage 'progress_percentage' => $progressPercentage
]); ]);
} }
public function saveRecordedAudioName(Request $request, $id_latihan) public function updateStatusBatch(Request $request)
{
// Validasi data yang masuk
$request->validate([
'recorded_audio' => 'required|string', // Nama file audio yang disimpan
]);
// Temukan latihan berdasarkan ID (tanpa memeriksa user_id)
$latihan = Latihan::find($id_latihan);
// Jika latihan ditemukan, simpan nama file rekaman ke kolom recorder_audio
if ($latihan) {
$latihan->recorder_audio = $request->recorded_audio;
$latihan->status = 'benar'; // Status latihan diubah menjadi selesai
$latihan->save();
return response()->json([
'message' => 'Rekaman berhasil disimpan!',
'latihan' => $latihan,
], 200);
} else {
return response()->json([
'message' => 'Latihan tidak ditemukan.',
], 404);
}
}
// API untuk menyimpan data progres latihan yang telah diselesaikan
public function saveProgress(Request $request, $submateri_id)
{
// Validasi data yang masuk
$request->validate([
'latihan_ids' => 'required|array', // Array dari ID latihan yang telah diselesaikan
'latihan_ids.*' => 'exists:latihan,id', // Pastikan ID latihan ada di tabel latihan
]);
// Ambil user_id dari token yang digunakan (auth)
$user_id = Auth::id(); // Mendapatkan ID pengguna yang terautentikasi
// Ambil data submateri untuk menampilkan title
$submateri = SubMateri::find($submateri_id);
if (!$submateri) {
return response()->json(['message' => 'SubMateri tidak ditemukan'], 404);
}
// Menyimpan progres untuk setiap latihan yang diselesaikan oleh pengguna
$savedProgress = [];
foreach ($request->latihan_ids as $latihanId) {
// Temukan latihan berdasarkan ID
$latihan = Latihan::find($latihanId);
// Jika latihan ditemukan, simpan ke tabel progres
if ($latihan) {
$progress = Progress::updateOrCreate(
[
'user_id' => $user_id, // Gunakan user_id dari token
'sub_materi_id' => $submateri_id,
'id_latihan' => $latihan->id,
],
[
'status' => 'menunggu', // Status latihan diatur sebagai 'menunggu' setelah selesai
'nilai' => $latihan->nilai, // Nilai latihan yang diberikan oleh pengajar
]
);
// Menambahkan data progres yang baru disimpan untuk ditampilkan
$savedProgress[] = [
'user_id' => $user_id,
'sub_materi_id' => $submateri_id,
'submateri_title' => $submateri->title, // Menambahkan title dari submateri
'status' => 'menunggu',
'id_latihan' => $latihan->id,
'potongan_ayat' => $latihan->potongan_ayat,
'latin_text' => $latihan->latin_text,
'recorder_audio' => $latihan->recorder_audio,
];
}
}
return response()->json([
'message' => 'Progres latihan berhasil disimpan.',
'progress' => $savedProgress,
], 200);
}
public function getHasilPenilaianByUserAndSubMateri($submateri_id)
{ {
// Ambil user_id dari token yang digunakan (auth) // Validasi input hanya array of ID
$user_id = Auth::id(); // Mendapatkan ID pengguna yang terautentikasi $request->validate([
'progress_ids' => 'required|array|min:1',
'progress_ids.*' => 'integer|exists:progress,id',
]);
// Ambil data berdasarkan user_id dan submateri_id $progressIds = $request->input('progress_ids');
$progressData = Progress::with([
'latihan' => function ($query) { // Update status menjadi 'menunggu'
$query->select('id', 'potongan_ayat', 'latin_text', 'recorder_audio', 'feedback_pengajar', 'status'); Progress::whereIn('id', $progressIds)->update(['status' => 'menunggu']);
}
return response()->json([
'message' => 'Status berhasil diubah menjadi menunggu.',
'updated_ids' => $progressIds,
]);
}
public function progressMenunggu()
{
$progressList = Progress::where('status', 'menunggu')
->with([
'user:id,nama_lengkap',
'subMateri:id,title,subtitle'
]) ])
->where('user_id', $user_id) // Mengambil progress berdasarkan user_id yang terautentikasi ->orderByDesc('created_at')
->where('sub_materi_id', $submateri_id) // Mengambil progress berdasarkan submateri_id ->get();
->get(['user_id', 'sub_materi_id', 'id_latihan', 'nilai']); // Pilih data yang diperlukan dari tabel progress
// Cek apakah ada data progress // Filter unik berdasarkan kombinasi user_id dan sub_materi_id
if ($progressData->isEmpty()) { $filtered = $progressList->unique(fn($item) => $item->user_id . '-' . $item->sub_materi_id);
return response()->json(['message' => 'Tidak ada data progres untuk pengguna ini pada submateri ini'], 404);
}
// Menyiapkan hasil yang akan dikembalikan $data = $filtered->map(function ($progress) {
$results = $progressData->map(function ($progress) {
return [ return [
'user_id' => $progress->user_id, 'user_id' => $progress->user_id,
'sub_materi_id' => $progress->sub_materi_id, 'sub_materi_id' => $progress->sub_materi_id,
'id_latihan' => $progress->id_latihan, 'nama_lengkap' => $progress->user->nama_lengkap,
'nilai' => $progress->nilai, 'title' => $progress->subMateri->title,
'potongan_ayat' => $progress->latihan->potongan_ayat, 'subtitle' => $progress->subMateri->subtitle,
'latin_text' => $progress->latihan->latin_text, 'status' => $progress->status,
'recorder_audio' => $progress->latihan->recorder_audio, 'created_at' => $progress->created_at,
'feedback_pengajar' => $progress->latihan->feedback_pengajar, // 'audio_file' => $progress->recorder_audio,
'status' => $progress->latihan->status,
]; ];
}); });
return response()->json([ return response()->json([
'message' => 'Data hasil penilaian berhasil diambil.', 'status' => true,
'data' => $results 'message' => 'Data progress santri dengan status menunggu',
'data' => $data->values(), // reset index
]);
}
public function getProgressLatihanByUserAndSubmateri($user_id, $sub_materi_id)
{
// Ambil data progress untuk user dan sub_materi_id yang diberikan
$progressList = Progress::where('user_id', $user_id)
->where('sub_materi_id', $sub_materi_id)
->with(['latihan', 'subMateri'])
->orderBy('updated_at', 'desc') // Mengurutkan berdasarkan updated_at secara descending
->limit(3) // Batasi hanya 3 data terbaru
->get();
// Jika data progress kosong, kembalikan respons bahwa tidak ada data
if ($progressList->isEmpty()) {
return response()->json([
'status' => false,
'message' => 'Tidak ada progress ditemukan',
'data' => []
]);
}
// Memetakan data progress untuk menambahkan status_validasi, feedback_pengajar, dan total_nilai
$data = $progressList->map(function ($item) {
return [
'id_progress' => $item->id,
'title' => $item->subMateri->title ?? '',
'subtitle' => $item->subMateri->subtitle ?? '',
'id_latihan' => $item->id_latihan,
'potongan_ayat' => $item->latihan->potongan_ayat ?? null,
'latin_text' => $item->latihan->latin_text ?? null,
'status' => $item->status, // Status dari progress
'status_validasi' => $item->status_validasi, // status_validasi yang ditambahkan
'feedback_pengajar' => $item->feedback_pengajar, // feedback_pengajar yang ditambahkan
'nilai' => $item->nilai, // Nilai dari progress
'recorder_audio' => $item->recorder_audio,
'total_nilai' => $item->total_nilai, // total_nilai yang ditambahkan
];
});
// Mengembalikan data progress latihan yang berhasil diambil beserta informasi tambahan
return response()->json([
'status' => true,
'message' => 'Data progress latihan berhasil diambil',
'data' => $data
]);
}
public function savePenilaian(Request $request)
{
// Validate the incoming request
$validatedData = $request->validate([
'id_progress' => 'required|array',
'id_progress.*' => 'exists:progress,id', // Ensure the progress id exists
'status_validasi' => 'required|array',
'status_validasi.*' => 'in:benar,salah', // Status must be 'benar' or 'salah'
'feedback_pengajar' => 'nullable|array',
'feedback_pengajar.*' => 'nullable|string',
'nilai' => 'required|array',
'nilai.*' => 'integer|min:0|max:100', // Ensure each nilai is between 0 and 100
]);
// Start the DB transaction
DB::beginTransaction();
try {
$totalNilaiSum = 0;
$nilaiCount = 0;
$updatedProgress = [];
// Loop through each id_progress to update each record individually
foreach ($validatedData['id_progress'] as $index => $id_progress) {
// Find the Progress record
$progress = Progress::findOrFail($id_progress);
// Update the progress record
$progress->status_validasi = $validatedData['status_validasi'][$index];
$progress->feedback_pengajar = $validatedData['feedback_pengajar'][$index] ?? null;
$progress->nilai = $validatedData['nilai'][$index];
$progress->save();
// Add nilai to the total sum for average calculation
$totalNilaiSum += $progress->nilai;
$nilaiCount++;
// Collect updated progress data
$updatedProgress[] = [
'id_progress' => $progress->id,
'status_validasi' => $progress->status_validasi,
'feedback_pengajar' => $progress->feedback_pengajar,
'nilai' => $progress->nilai,
];
}
// Calculate the total_nilai (average)
$total_nilai = $nilaiCount > 0 ? $totalNilaiSum / $nilaiCount : 0;
// Determine the status based on total_nilai
$status = ($total_nilai > 70) ? 'selesai' : 'gagal';
// Update total_nilai and status for each id_progress
foreach ($validatedData['id_progress'] as $index => $id_progress) {
$progress = Progress::findOrFail($id_progress);
$progress->total_nilai = $total_nilai;
$progress->status = $status; // Set the status based on the total_nilai
$progress->save();
}
// Commit the transaction
DB::commit();
// Return the updated progress data along with total_nilai and status
return response()->json([
'status' => true,
'message' => 'Penilaian berhasil disimpan',
'data' => [
'total_nilai' => $total_nilai,
'status' => $status, // Include status in the response
'progress' => $updatedProgress,
]
], 200); ], 200);
} catch (\Exception $e) {
// Rollback the transaction in case of error
DB::rollBack();
return response()->json([
'status' => false,
'message' => 'Terjadi kesalahan saat menyimpan penilaian',
'error' => $e->getMessage(),
], 500);
}
} }
} }

View File

@ -4,27 +4,40 @@
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\Progress;
class UploadAudioController extends Controller class UploadAudioController extends Controller
{ {
public function uploadAudio(Request $request) public function uploadAudio(Request $request)
{ {
print($request->file('recorded_audio'));
// Validasi file // Validasi file
$request->validate([ $request->validate([
// 'recorded_audio' => 'required|mimes:m4a,mp3,wav|max:10240', // Batasi ukuran file (misalnya 10MB) 'recorded_audio' => 'required|max:10240', // Batasi ukuran file (misalnya 10MB)
'recorded_audio' => 'required|mimes:m4a,mp3,wav', // Batasi ukuran file (misalnya 10MB) 'sub_materi_id' => 'required|exists:sub_materi,id', // Validasi sub_materi_id
'id_latihan' => 'required|exists:latihan,id', // Validasi id_latihan
]); ]);
// Mendapatkan user_id dari autentikasi token
$user_id = auth()->user()->id;
// Mendapatkan file dari request // Mendapatkan file dari request
$file = $request->file('recorded_audio'); $file = $request->file('recorded_audio');
// Menyimpan file di disk public // Menyimpan file di disk public
$path = $file->store('audio_files', 'public'); // Menyimpan di folder `storage/app/public/audio_files` $path = $file->store('audio_files', 'public'); // Menyimpan di folder `storage/app/public/audio_files`
// Menyimpan data ke tabel Progress
$progress = Progress::create([
'user_id' => $user_id,
'sub_materi_id' => $request->sub_materi_id,
'id_latihan' => $request->id_latihan,
'recorder_audio' => $path, // Simpan path file audio
]);
return response()->json([ return response()->json([
'message' => 'File berhasil diupload!', 'message' => 'File berhasil diupload dan data progress tersimpan!',
'file_path' => $path 'file_path' => $path,
'progress' => $progress,
], 200); ], 200);
} }
} }

View File

@ -121,6 +121,7 @@ public function loginWithTelp(Request $request)
'access_token' => $tokenResult, 'access_token' => $tokenResult,
'token_type' => 'Bearer', 'token_type' => 'Bearer',
'user' => [ 'user' => [
'id' => $user->id,
'peran' => $user->peran // Ensure 'peran' is included here 'peran' => $user->peran // Ensure 'peran' is included here
] ]
], 'Authenticated'); ], 'Authenticated');
@ -496,22 +497,23 @@ public function getUserProgres(Request $request)
$progresData = Progress::where('user_id', $santri->id) $progresData = Progress::where('user_id', $santri->id)
->where('status', 'selesai') // Menghitung submateri yang sudah selesai ->where('status', 'selesai') // Menghitung submateri yang sudah selesai
->with('submateri') // Load data submateri yang terkait ->with('submateri') // Load data submateri yang terkait
->get(); ->orderBy('updated_at', 'desc') // Ambil yang terakhir berdasarkan updated_at
->first(); // Ambil hanya satu data yang terbaru (progres terakhir)
// Hitung jumlah submateri yang selesai // Cek jika progres ada dan sudah selesai
$completedSubmateriCount = $progresData->count(); // Menghitung jumlah data progres yang selesai if ($progresData) {
// Hanya tampilkan santri yang telah menyelesaikan submateri
if ($completedSubmateriCount > 0) {
return [ return [
'user_id' => $santri->id, 'user_id' => $santri->id,
'nama_lengkap' => $santri->nama_lengkap, 'nama_lengkap' => $santri->nama_lengkap,
'no_telp_wali' => $santri->no_telp_wali, 'no_telp_wali' => $santri->no_telp_wali,
'completed_submateri' => $completedSubmateriCount, // Jumlah submateri yang telah selesai 'completed_submateri' => 1, // Hanya tampilkan yang sudah selesai
'status' => $progresData->status,
'nilai' => $progresData->nilai,
'updated_at' => $progresData->updated_at,
]; ];
} }
})->filter(function ($santri) { })->filter(function ($santri) {
return $santri !== null; // Filter out santri that haven't completed any submateri return $santri !== null; // Filter out santri yang tidak ada progres yang selesai
}); });
// Jika tidak ada santri yang menyelesaikan submateri // Jika tidak ada santri yang menyelesaikan submateri
@ -539,4 +541,5 @@ public function getUserProgres(Request $request)
} }

View File

@ -20,9 +20,9 @@ class Latihan extends Model
'materi_description', 'materi_description',
'correct_audio', 'correct_audio',
'recorder_audio', 'recorder_audio',
'feedback_pengajar', // 'feedback_pengajar',
'nilai', // 'nilai',
'status', // 'status',
]; ];
public function progress() public function progress()

View File

@ -9,18 +9,17 @@ class Progress extends Model
{ {
use HasFactory; use HasFactory;
protected $fillable = ['user_id', 'sub_materi_id', 'status', 'nilai','id_latihan',]; protected $fillable = ['user_id', 'sub_materi_id', 'status', 'nilai','id_latihan','recorder_audio','status_validasi','feedback_pengajar','total_nilai',];
// Relasi ke User // Relasi ke User
public function user() public function user() {
{ return $this->belongsTo(User::class, 'user_id');
return $this->belongsTo(User::class); }
}
// Relasi ke SubMateri // Relasi ke SubMateri
public function subMateri() public function subMateri()
{ {
return $this->belongsTo(SubMateri::class); return $this->belongsTo(SubMateri::class,'sub_materi_id');
} }
// Relasi ke Latihan // Relasi ke Latihan

View File

@ -30,4 +30,9 @@ public function kategori()
{ {
return $this->belongsTo(Kategori::class, 'id_kategori'); return $this->belongsTo(Kategori::class, 'id_kategori');
} }
public function progresses()
{
return $this->hasMany(Progress::class);
}
} }

View File

@ -49,4 +49,9 @@ class User extends Authenticatable
protected $casts = [ protected $casts = [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
]; ];
public function progresses()
{
return $this->hasMany(Progress::class);
}
} }

View File

@ -0,0 +1,30 @@
<?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::table('latihan', function (Blueprint $table) {
// Menghapus kolom 'recorded_audio'
$table->dropColumn('recorder_audio');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('latihan', function (Blueprint $table) {
// Menambahkan kembali kolom 'recorded_audio' jika rollback dilakukan
$table->string('recorder_audio')->nullable();
});
}
};

View File

@ -0,0 +1,30 @@
<?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::table('progress', function (Blueprint $table) {
// Menambahkan kolom 'recorded_audio' dengan tipe data string
$table->string('recorder_audio')->nullable(); // Anda bisa menyesuaikan tipe data sesuai kebutuhan
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('progress', function (Blueprint $table) {
// Menghapus kolom 'recorded_audio' jika rollback dilakukan
$table->dropColumn('recorded_audio');
});
}
};

View File

@ -0,0 +1,30 @@
<?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::table('latihan', function (Blueprint $table) {
$table->dropColumn(['feedback_pengajar', 'nilai', 'status']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('latihan', function (Blueprint $table) {
$table->text('feedback_pengajar')->nullable();
$table->integer('nilai')->nullable();
$table->string('status', 20)->nullable();
});
}
};

View File

@ -0,0 +1,30 @@
<?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::table('progress', function (Blueprint $table) {
$table->enum('status_validasi', ['benar', 'salah'])->nullable(); // status_validasi field
$table->text('feedback_pengajar')->nullable(); // feedback_pengajar field
$table->integer('total_nilai')->nullable(); // total_nilai field
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('progress', function (Blueprint $table) {
$table->dropColumn(['status_validasi', 'feedback_pengajar', 'total_nilai']);
});
}
};

View File

@ -73,9 +73,18 @@
Route::get('/progres/presentase', [ProgressController::class, 'getProgressPercentage']); Route::get('/progres/presentase', [ProgressController::class, 'getProgressPercentage']);
Route::get('/progres/{user_id}', [ProgressController::class, 'getProgressPercentageById']); Route::get('/progres/{user_id}', [ProgressController::class, 'getProgressPercentageById']);
Route::put('latihan/{id_latihan}/saverecord', [ProgressController::class, 'saveRecordedAudioName']); Route::put('latihan/{id_latihan}/saverecord', [ProgressController::class, 'saveRecordedAudioName']);
Route::post('progress/{submateri_id}/save', [ProgressController::class, 'saveProgress']); // Route::post('progress/{submateri_id}/save', [ProgressController::class, 'saveProgress']);
Route::get('/hasil-penilaian/{submateri_id}', [ProgressController::class, 'getHasilPenilaianByUserAndSubMateri']); Route::get('/hasil-penilaian/{submateri_id}', [ProgressController::class, 'getHasilPenilaianByUserAndSubMateri']);
// Route::post('/upload_audio', [UploadAudioController::class, 'uploadAudio']);
Route::post('/upload_audio', [UploadAudioController::class, 'uploadAudio']); Route::post('/upload_audio', [UploadAudioController::class, 'uploadAudio']);
Route::post('/update_progress_status', [ProgressController::class, 'updateStatusBatch']);
Route::get('/progress/menunggu', [ProgressController::class, 'progressMenunggu']);
Route::get('/progress/latihan/{user_id}/{sub_materi_id}', [ProgressController::class, 'getProgressLatihanByUserAndSubmateri']);
Route::post('save_penilaian', [ProgressController::class, 'savePenilaian']);
}); });