diff --git a/admin/api/config_api.php b/admin/api/config_api.php new file mode 100644 index 0000000..28b00c0 --- /dev/null +++ b/admin/api/config_api.php @@ -0,0 +1,5 @@ + diff --git a/admin/api/ocr_helper.php b/admin/api/ocr_helper.php new file mode 100644 index 0000000..283596f --- /dev/null +++ b/admin/api/ocr_helper.php @@ -0,0 +1,95 @@ + false, 'message' => 'Invalid request method']); + exit; +} + +if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) { + echo json_encode(['success' => false, 'message' => 'Upload failed or no image provided']); + exit; +} + +$type = $_POST['type'] ?? 'ktp'; // 'ktp' or 'kk' +$imagePath = $_FILES['image']['tmp_name']; +$imageData = base64_encode(file_get_contents($imagePath)); +$mimeType = mime_content_type($imagePath); + +// Construct Prompt based on Type +if ($type === 'kk') { + $promptText = "Extract strictly from this Indonesian Family Card (Kartu Keluarga). Return ONLY a raw JSON object with these keys: 'no_kk' (16 digits), 'kepala_keluarga' (Name), 'alamat', 'rt', 'rw', 'desa', 'kecamatan', 'kabupaten', 'provinsi', 'kode_pos', 'anggota': [ { 'nik': '...', 'nama': '...', 'hubungan': '...' } ] (Array of all family members found in the table. 'hubungan' examples: KEPALA KELUARGA, ISTRI, ANAK, FAMILI LAIN). Value must be string. If not found, use empty string."; +} else { + // Default to KTP + $promptText = "Extract data from this Indonesian KTP. Return ONLY a raw JSON object with keys: 'nik' (16 digits), 'nama' (Name), 'tempat_lh' (Place of Birth), 'tgl_lh' (YYYY-MM-DD), 'je_kel' (LK/PR), 'alamat' (Street only), 'rt', 'rw', 'desa' (Kel/Desa), 'kecamatan', 'kabupaten' (or Kota), 'provinsi', 'agama', 'status' (Sudah/Belum/Cerai Hidup/Cerai Mati), 'pekerjaan', 'kewarganegaraan' (WNI/WNA). Clean up text. If field is unclear, return empty string."; +} + +// Payload for Gemini +$data = [ + "contents" => [ + [ + "parts" => [ + ["text" => $promptText], + [ + "inline_data" => [ + "mime_type" => $mimeType, + "data" => $imageData + ] + ] + ] + ] + ] +]; + +// Send Request +$ch = curl_init(GEMINI_API_URL . '?key=' . GEMINI_API_KEY); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_POST, true); +curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); +curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + +$response = curl_exec($ch); +$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); +$error = curl_error($ch); +curl_close($ch); + +if ($error) { + echo json_encode(['success' => false, 'message' => 'CURL Error: ' . $error]); + exit; +} + +$result = json_decode($response, true); + +// Debugging check +if (isset($result['error'])) { + echo json_encode(['success' => false, 'message' => 'API Error: ' . $result['error']['message']]); + exit; +} + +if (!isset($result['candidates'][0]['content']['parts'][0]['text'])) { + echo json_encode(['success' => false, 'message' => 'No text returned from AI', 'raw' => $result]); + exit; +} + +// Parse AI Response +$rawText = $result['candidates'][0]['content']['parts'][0]['text']; + +// Remove Markdown Code Blocks if any (```json ... ```) +$cleanJson = preg_replace('/^```json\s*|\s*```$/', '', trim($rawText)); +$parsedData = json_decode($cleanJson, true); + +if (json_last_error() !== JSON_ERROR_NONE) { + // Fallback: try to find JSON object structure in text + if (preg_match('/\{.*\}/s', $cleanJson, $matches)) { + $parsedData = json_decode($matches[0], true); + } +} + +if (!$parsedData) { + echo json_encode(['success' => false, 'message' => 'Failed to parse AI response', 'raw_text' => $rawText]); + exit; +} + +echo json_encode(['success' => true, 'data' => $parsedData]); +?> diff --git a/admin/kartu/add_kartu.php b/admin/kartu/add_kartu.php index 9c6115c..d2218f8 100644 --- a/admin/kartu/add_kartu.php +++ b/admin/kartu/add_kartu.php @@ -6,10 +6,49 @@