From 70198dde563cf1037486cf8cc72c207a6cc807ea Mon Sep 17 00:00:00 2001 From: ramzdhani11 <158116439+ramzdhani11@users.noreply.github.com> Date: Wed, 15 Apr 2026 01:18:54 +0700 Subject: [PATCH] update fitur klasifikasi --- app/Http/Controllers/TomatController.php | 144 +++++++++++ composer.json | 3 +- resources/views/landing_page/result.blade.php | 125 --------- resources/views/landing_page/upload.blade.php | 243 ------------------ .../tomat/classification_result.blade.php | 136 ++++++++++ resources/views/tomat/upload.blade.php | 236 +++++++++++++++++ resources/views/welcome.blade.php | 4 +- routes/## GitHub Copilot Chat.md | 97 +++++++ routes/web.php | 33 ++- 9 files changed, 645 insertions(+), 376 deletions(-) create mode 100644 app/Http/Controllers/TomatController.php delete mode 100644 resources/views/landing_page/result.blade.php delete mode 100644 resources/views/landing_page/upload.blade.php create mode 100644 resources/views/tomat/classification_result.blade.php create mode 100644 resources/views/tomat/upload.blade.php create mode 100644 routes/## GitHub Copilot Chat.md diff --git a/app/Http/Controllers/TomatController.php b/app/Http/Controllers/TomatController.php new file mode 100644 index 0000000..e1cb3d2 --- /dev/null +++ b/app/Http/Controllers/TomatController.php @@ -0,0 +1,144 @@ +validate([ + 'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:16384', + ]); + + try { + $uploadedFile = $request->file('image'); + + // Kirim file langsung ke Flask tanpa resize ulang di sini + // Flask sudah handle resize 256x256 sendiri + $response = Http::timeout(30) + ->attach( + 'image', + file_get_contents($uploadedFile->getRealPath()), + $uploadedFile->getClientOriginalName() + ) + ->post($this->apiUrl); + + if ($response->failed()) { + return back()->with('error', 'Gagal menghubungi API klasifikasi. Status: ' . $response->status()); + } + + $result = $response->json(); + + if (!isset($result['success']) || !$result['success']) { + $errorMsg = $result['message'] ?? 'Prediksi gagal'; + return back()->with('error', 'Error dari API: ' . $errorMsg); + } + + // Simpan hasil ke session + session([ + 'prediction_result' => $result, + 'processed_at' => now(), + 'original_size' => $uploadedFile->getSize(), + ]); + + return redirect()->route('tomat.result'); + + } catch (\Illuminate\Http\Client\ConnectionException $e) { + return back()->with('error', 'Tidak dapat terhubung ke API Flask. Pastikan server Python sudah berjalan (python app.py).'); + } catch (\Exception $e) { + return back()->with('error', 'Terjadi kesalahan: ' . $e->getMessage()); + } + } + + /** + * Show prediction result + */ + public function getResult() + { + $result = session('prediction_result'); + $processedAt = session('processed_at'); + + if (!$result) { + return redirect()->route('tomat.upload') + ->with('error', 'Tidak ada hasil prediksi. Silakan upload gambar terlebih dahulu.'); + } + + // ✅ FIX: nama view disesuaikan dengan file yang ada + return view('tomat.classification_result', compact('result', 'processedAt')); + } + + /** + * Check API service status + * Timeout 3 detik sudah cukup untuk health check lokal + */ + public function checkService() + { + try { + $response = Http::timeout(3)->get('http://127.0.0.1:5000/health'); + + if ($response->successful()) { + $health = $response->json(); + return response()->json([ + 'success' => true, + 'status' => 'online', + 'service' => $health['service'] ?? 'Tomat Classification API', + 'model_loaded' => $health['model_loaded'] ?? false + ]); + } + + return response()->json([ + 'success' => false, + 'status' => 'offline', + 'message' => 'API Flask tidak tersedia' + ], 503); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'status' => 'error', + 'message' => 'Tidak dapat terhubung ke API Flask: ' . $e->getMessage() + ], 503); + } + } + + /** + * Get model info dari Flask API + */ + public function getModelInfo() + { + try { + $response = Http::timeout(5)->get('http://127.0.0.1:5000/info'); + + if ($response->successful()) { + return response()->json($response->json()); + } + + return response()->json([ + 'success' => false, + 'message' => 'Gagal mengambil info model' + ], 503); + + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'message' => $e->getMessage() + ], 503); + } + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 44c6054..99639df 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "require": { "php": "^8.2", "laravel/framework": "^12.0", - "laravel/tinker": "^2.10.1" + "laravel/tinker": "^2.10.1", + "intervention/image": "^2.7" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/resources/views/landing_page/result.blade.php b/resources/views/landing_page/result.blade.php deleted file mode 100644 index eb85a55..0000000 --- a/resources/views/landing_page/result.blade.php +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - Hasil Klasifikasi - Maturity Scan Tomat - - - - - - - - - - - - - - - - -
-
- -
- -
-

- Hasil Klasifikasi Tomat -

-
- - - @if($imagePath) -
- Tomato Image -
- @endif - - -
- -
- Kematangan: - - {{ ucfirst($category) }} - -
- - -
-
- Probabilitas: - {{ $probability }}% -
- - -
-
-
-
-
- - -
-

- {{ $description }} -

-
-
- - - -
-
-
- - - - - diff --git a/resources/views/landing_page/upload.blade.php b/resources/views/landing_page/upload.blade.php deleted file mode 100644 index 1e79033..0000000 --- a/resources/views/landing_page/upload.blade.php +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - Unggah Gambar Tomat - Tomato Maturity Scan - - - - - - - - - - - - - - - - - - -
-
- -
-

- Unggah Gambar Tomat Anda -

-

- Seret dan lepas gambar tomat Anda atau klik untuk memilih file. -

-
- -
- @csrf - - - @if ($errors->any()) -
-
    - @foreach ($errors->all() as $error) -
  • {{ $error }}
  • - @endforeach -
-
- @endif - - - @if (session('success')) -
- {{ session('success') }} -
- @endif - - -
-
- - - -
- - - -
- -

Seret & Lepas Gambar

-

atau klik untuk mengunggah

-

Format JPG atau PNG

-
- - - -
- -

- Pastikan gambar jelas dan tidak blur. -

- -
- -
-
- -
-
- - - - - - - - - diff --git a/resources/views/tomat/classification_result.blade.php b/resources/views/tomat/classification_result.blade.php new file mode 100644 index 0000000..9b70b42 --- /dev/null +++ b/resources/views/tomat/classification_result.blade.php @@ -0,0 +1,136 @@ + + + + + + Hasil Klasifikasi - Maturity Scan Tomat + + + + + + + + + +
+
+ + @if (session('error')) +
+ +

{{ session('error') }}

+
+ @endif + + @if(isset($result) && $result['success']) + @php + $predictionClass = $result['prediction']['class']; + $confidence = $result['prediction']['confidence_percentage']; + $probabilities = $result['prediction']['probabilities']; + $metadata = $result['metadata']; + @endphp + +
+
+

Hasil Klasifikasi Tomat

+
+ +
+
+ @if($predictionClass == 'matang') +
🍅
+ Matang + @elseif($predictionClass == 'mentah') +
🟢
+ Mentah + @else +
🟡
+ Setengah Matang + @endif +
+ +
+
+ Tingkat Kepercayaan + {{ number_format($confidence, 1) }}% +
+
+
+
+
+ +
+

Probabilitas Setiap Kelas

+
+ @foreach($probabilities as $class => $prob) +
+

+ {{ ucfirst(str_replace('_', ' ', $class)) }} +

+
{{ number_format($prob['percentage'], 1) }}%
+
{{ number_format($prob['probability'], 4) }}
+
+ @endforeach +
+
+ +
+

Informasi Proses

+
+
Model: {{ $metadata['model_type'] ?? 'RandomForest' }}
+
Waktu Proses: {{ $metadata['processing_time_seconds'] ?? '-' }} detik
+
Fitur: {{ $metadata['features_used'] ?? '-' }}
+
Diproses: {{ $processedAt ? $processedAt->format('d M Y, H:i:s') : '-' }}
+
+
+ + +
+
+ + @else +
+
+ +

Tidak Ada Hasil

+

Silakan upload gambar tomat terlebih dahulu.

+ + Upload Gambar + +
+
+ @endif + +
+
+ + + + + \ No newline at end of file diff --git a/resources/views/tomat/upload.blade.php b/resources/views/tomat/upload.blade.php new file mode 100644 index 0000000..2577d29 --- /dev/null +++ b/resources/views/tomat/upload.blade.php @@ -0,0 +1,236 @@ + + + + + + Klasifikasi Kematangan Tomat + + + + + +
+
+

+ Klasifikasi Kematangan Tomat +

+

Sistem Klasifikasi Tingkat Kematangan Tomat menggunakan Machine Learning

+
+ + +
+ Memeriksa status layanan... +
+ + + @if (session('error')) +
{{ session('error') }}
+ @endif + @if (session('success')) +
{{ session('success') }}
+ @endif + +
+
+
+
+

+ Upload Gambar Tomat +

+ + {{-- ✅ FIX UTAMA: Pakai form biasa (method POST), bukan AJAX fetch --}} + {{-- Controller sudah return redirect(), bukan JSON --}} +
+ @csrf + + +
+ +

Drag & Drop Gambar di sini

+

atau klik untuk memilih file

+ + +

Format: PNG, JPG, JPEG (Max: 16MB)

+
+ + + +
+ + +
+
+ Loading... +
+

Sedang memproses gambar...

+
+
+
+ + +
+
+
+
+ +
Machine Learning
+

Menggunakan Random Forest untuk klasifikasi akurat

+
+
+
+
+
+
+ +
Color Histogram
+

Ekstraksi fitur RGB 8x8x8 untuk analisis warna

+
+
+
+
+
+
+ +
Real-time
+

Proses klasifikasi cepat dan hasil instan

+
+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index d6d78bc..d373ae5 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -28,7 +28,7 @@