acne-api-hf / rules.py
ItsFarros's picture
fix: severity tanpa nodul untuk treatment lesi lain
33a7a4f verified
Raw
History Blame Contribute Delete
33.5 kB
"""
Forward Chaining Expert System - 7 Rules
Berdasarkan PERDOSKI 2024 (Panduan Praktik Klinis)
Referensi: Tabel 1. Rekomendasi Terapi Akne IAEM 2015
Structure:
Layer 1: Treatment (7 Rules) - Prioritas utama
Layer 2: Daily Skincare (Pendukung) - Facial wash, moisturizer, sunscreen
Input:
- skin_type: "berminyak" | "kering" | "sensitif" | "kombinasi"
- severity_level: "clear" | "mild" | "moderate" | "severe"
- dominant_lesion: "comedone" | "papula_pustula" | "nodul"
Klasifikasi Derajat (PERDOSKI 2024):
- Ringan: Komedo <20 ATAU lesi inflamasi <15 ATAU total lesi 30
- Sedang: Komedo 20-100 ATAU lesi inflamasi 15-50 ATAU total lesi 30-125
- Berat: Kista >5 ATAU komedo >100 ATAU lesi inflamasi >50 ATAU total lesi >125
Keterangan singkatan (PERDOSKI 2024):
RA = Retinoic Acid (termasuk Adapalene)
BPO = Benzoyl Peroxide
SA = Salicylic Acid
AB = Antibiotik (Doksisiklin/Minosiklin)
AA = Azelaic Acid
Dox = Doxicyclin
Status Legalitas Indonesia (BPOM):
- Salicylic Acid ≤2%: Obat Bebas (OTC), batas skincare BPOM maks 2%
- Adapalene 0.1%: Obat Keras (Resep Dokter), walaupun di US sudah OTC
- Benzoyl Peroxide 2.5-5%: Obat Bebas Terbatas (OTC terbatas)
- Azelaic Acid 15-20%: Obat Keras (Resep Dokter), ASEAN Cosmetic Directive
- Niacinamide 2-10%: Kosmetik (Bebas), tidak termasuk obat
"""
VALID_SKIN_TYPES = ["berminyak", "kering", "sensitif", "kombinasi"]
SEVERITY_TO_DEGREE = {
"clear": "bersih",
"mild": "ringan",
"moderate": "sedang",
"severe": "berat",
}
# ═══════════════════════════════════════════════════════════════════
# REFERENSI BAHAN AKTIF (untuk metadata)
# ═══════════════════════════════════════════════════════════════════
ACTIVE_INGREDIENT_INFO = {
"salicylic_acid": {
"name": "Salicylic Acid (BHA)",
"otc_status": "Obat Bebas (OTC)",
"prescription_required": False,
"pregnancy_category": "Hati-hati (konsultasi dokter)",
"max_concentration_otc": "2%",
"drug_interactions": [
"Hindari penggunaan bersamaan dengan Adapalene (risiko iritasi berlebih)",
"BPO + SA: gunakan bergantian waktu (pagi/malam), jangan bersamaan",
],
},
"adapalene": {
"name": "Adapalene 0.1%",
"otc_status": "Obat Keras (Resep Dokter)",
"prescription_required": True,
"pregnancy_category": "Kategori C (Hindari saat hamil)",
"max_concentration_otc": "N/A (obat keras di Indonesia)",
"drug_interactions": [
"JANGAN dicampur dengan Salicylic Acid (iritasi berlebih)",
"HINDARI penggunaan bersamaan dengan Retinoid lain, Sulfur, atau AHA",
"Adapalene + BPO: hanya dalam produk kombinasi (Epiduo) di bawah pengawasan dokter",
],
},
"benzoyl_peroxide": {
"name": "Benzoyl Peroxide (BPO)",
"otc_status": "Obat Bebas Terbatas",
"prescription_required": False,
"pregnancy_category": "Hati-hati (konsultasi dokter)",
"max_concentration_otc": "5%",
"drug_interactions": [
"HINDARI dengan Tretinoin, Isotretinoin, Tazarotene (menurunkan efektivitas + iritasi)",
"Hindari dengan Sulfonamida (perubahan warna kulit sementara)",
"BPO + SA: gunakan bergantian waktu, jangan bersamaan",
],
},
"azelaic_acid": {
"name": "Azelaic Acid 15-20%",
"otc_status": "Obat Keras (Resep Dokter)",
"prescription_required": True,
"pregnancy_category": "Kategori B (Relatif aman, konsultasi dokter)",
"max_concentration_otc": "N/A (obat keras di Indonesia/ASEAN)",
"drug_interactions": [
"HINDARI dengan Retinol/Isotretinoin (meningkatkan iritasi)",
"HINDARI dengan produk astringent atau alkohol kuat",
"AA + SA: aman, bahkan ada produk kombinasi",
],
},
"niacinamide": {
"name": "Niacinamide 2-10%",
"otc_status": "Kosmetik (Bebas)",
"prescription_required": False,
"pregnancy_category": "Aman",
"max_concentration_otc": "Tidak dibatasi (produk kosmetik)",
"drug_interactions": [
"Aman dengan hampir semua bahan aktif",
"Tidak bereaksi dengan bahan sunscreen (oxybenzone, avobenzone, titanium dioxide)",
"Meningkatkan efektivitas Vitamin C dan sunscreen",
],
},
}
# ═══════════════════════════════════════════════════════════════════
# LAYER 1: TREATMENT (7 Rules - PERDOSKI 2024 Tabel 1)
# ═══════════════════════════════════════════════════════════════════
RULES = [
# ── RINGAN: Komedo + Berminyak/Kombinasi ──
# PERDOSKI: Topikal lini 1 → SA (2B) atau RA (1A)
{
"rule": 1,
"condition": {
"degree": ["ringan"],
"dominant_lesion": ["comedone"],
"skin_type": ["berminyak", "kombinasi"],
},
"recommendation": {
"active_ingredient": "Salicylic Acid (BHA) 0.5-2%",
"product_type": "Treatment",
"dosage": "2-3 tetes atau secukupnya",
"frequency": "1-2x sehari (pagi dan/atau malam)",
"time_of_day": "pagi_malam",
"instructions": (
"Oleskan tipis pada area jerawat setelah facial wash. "
"BHA (Salicylic Acid) larut dalam minyak, membersihkan pori-pori tersumbat dari dalam. "
"Dapat digunakan pagi atau malam. Mulai dari konsentrasi rendah (0.5%) lalu tingkatkan."
),
"evidence": "2B (PERDOSKI 2024)",
"prescription_required": False,
"otc_status": "Obat Bebas (OTC) - tersedia tanpa resep",
"pregnancy_category": "Hati-hati - konsultasi dokter sebelum digunakan",
"drug_interactions": [
"Hindari penggunaan bersamaan dengan Adapalene (risiko iritasi berlebih)",
"Jangan digunakan bersamaan dengan BPO di waktu yang sama",
],
},
"contraindications": [],
},
# ── RINGAN: Komedo + Sensitif/Kering ──
# PERDOSKI: Topikal lini 1 → RA (1A) atau SA (2B), pilih yang lebih lembut
{
"rule": 2,
"condition": {
"degree": ["ringan"],
"dominant_lesion": ["comedone"],
"skin_type": ["sensitif", "kering"],
},
"recommendation": {
"active_ingredient": "Dermokosmetik Multitarget (BHA lembut + Niacinamide)",
"product_type": "Treatment",
"dosage": "Secukupnya",
"frequency": "1-2x sehari (pagi dan malam)",
"time_of_day": "pagi_malam",
"instructions": (
"Gunakan produk multitarget dengan bahan aktif lembut. "
"Formula hypoallergenic untuk kulit sensitif. "
"Dapat mengandung BHA lembut (0.5%) atau Niacinamide untuk eksfoliasi ringan tanpa iritasi."
),
"evidence": "1A-2B (PERDOSKI 2024)",
"prescription_required": False,
"otc_status": "Kosmetik/Obat Bebas - tersedia tanpa resep",
"pregnancy_category": "Aman (Niacinamide) / Hati-hati (BHA rendah)",
"drug_interactions": [],
},
"contraindications": [],
},
# ── RINGAN: Papula/Pustula + Berminyak ──
# PERDOSKI: Topikal lini 1 → RA (1A) dan/atau BPO (1A)
{
"rule": 3,
"condition": {
"degree": ["ringan"],
"dominant_lesion": ["papula_pustula"],
"skin_type": ["berminyak", "kombinasi"],
},
"recommendation": {
"active_ingredient": "Adapalene 0.1% (Retinoid) atau Benzoyl Peroxide 2.5-5%",
"product_type": "Treatment",
"dosage": "Pea-size untuk seluruh wajah (Adapalene) atau secukupnya (BPO)",
"frequency": "1x sehari (malam)",
"time_of_day": "malam",
"instructions": (
"Pilihan 1: Adapalene 0.1% malam hari. Mulai 2-3x seminggu, tingkatkan bertahap. "
"Pilihan 2: Benzoyl Peroxide 2.5-5% malam hari. "
"Gunakan pelembap setelahnya. Hindari paparan sinar UV langsung."
),
"evidence": "1A (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Adapalene: Obat Keras (Resep Dokter). BPO: Obat Bebas Terbatas.",
"pregnancy_category": "Adapalene: Kategori C (HINDARI saat hamil). BPO: Hati-hati.",
"drug_interactions": [
"Adapalene: JANGAN dicampur dengan SA, Retinoid lain, atau Sulfur",
"Adapalene + BPO: hanya dalam produk kombinasi (Epiduo) di bawah pengawasan dokter",
"BPO: Hindari dengan Tretinoin/Isotretinoin",
],
},
"contraindications": [
"ADAPALENE: Hanya dengan resep dokter",
"Tidak untuk ibu hamil (Adapalene - Kategori C)",
"Hindari paparan sinar UV langsung",
"Jangan dicampur sendiri Adapalene dengan bahan aktif lain",
],
},
# ── RINGAN: Papula/Pustula + Sensitif/Kering ──
# PERDOSKI: Topikal lini 1 → RA (1A) atau SA (2B)
{
"rule": 4,
"condition": {
"degree": ["ringan"],
"dominant_lesion": ["papula_pustula"],
"skin_type": ["sensitif", "kering"],
},
"recommendation": {
"active_ingredient": "Dermokosmetik (pagi) + Adapalene 0.1% (malam)",
"product_type": "Treatment",
"dosage": "Secukupnya (dermokosmetik), pea-size (Adapalene)",
"frequency": "2x sehari (pagi dermokosmetik, malam adapalene)",
"time_of_day": "pagi_malam",
"instructions": (
"Pagi: Dermokosmetik multitarget untuk menenangkan kulit. "
"Malam: Adapalene 0.1% mulai 2-3x seminggu, tingkatkan bertahap. "
"Gunakan pelembap setelah adapalene."
),
"evidence": "1A (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Adapalene: Obat Keras (Resep Dokter)",
"pregnancy_category": "Adapalene: Kategori C (HINDARI saat hamil)",
"drug_interactions": [
"Adapalene: JANGAN dicampur dengan SA, Retinoid lain, Sulfur, atau AHA",
"Hindari produk iritatif lainnya saat menggunakan Adapalene",
],
},
"contraindications": [
"ADAPALENE: Hanya dengan resep dokter",
"Tidak untuk ibu hamil (Adapalene - Kategori C)",
"Hindari produk iritatif lainnya",
"Jangan dicampur sendiri Adapalene dengan bahan aktif lain",
],
},
# ── SEDANG: Papula/Pustula + Berminyak/Kombinasi ──
# PERDOSKI: Topikal lini 1 → RA (1A) dan/atau BPO (1A) dan AB (1A)
{
"rule": 5,
"condition": {
"degree": ["sedang"],
"dominant_lesion": ["papula_pustula"],
"skin_type": ["berminyak", "kombinasi"],
},
"recommendation": {
"active_ingredient": "Adapalene 0.1% + Benzoyl Peroxide 2.5% (kombinasi)",
"product_type": "Treatment",
"dosage": "Pea-size untuk seluruh wajah",
"frequency": "1x sehari (malam)",
"time_of_day": "malam",
"instructions": (
"Gunakan produk kombinasi Adapalene 0.1% + Benzoyl Peroxide 2.5% (misal: Epiduo). "
"Oleskan tipis pada seluruh wajah malam hari. "
"Mulai 3-4x seminggu, tingkatkan bertahap. "
"Gunakan pelembap 20 menit setelahnya."
),
"evidence": "1A (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Obat Keras (Resep Dokter) - produk kombinasi hanya dengan resep",
"pregnancy_category": "Kategori C (HINDARI saat hamil)",
"drug_interactions": [
"JANGAN gunakan bersamaan dengan produk retinoid lainnya",
"Hindari penggunaan bersamaan dengan SA atau Sulfur",
"Hanya gunakan produk kombinasi jadi (Epiduo), JANGAN dicampur sendiri",
],
},
"contraindications": [
"PRODUK KOMBINASI: Hanya dengan resep dokter",
"Tidak untuk ibu hamil (Adapalene - Kategori C)",
"Hindari paparan sinar UV langsung",
"JANGAN dicampur sendiri dengan produk retinoid lain",
],
},
# ── SEDANG: Papula/Pustula + Sensitif/Kering ──
# PERDOSKI: Topikal lini 2 → AA (1B) atau SA (1B)
{
"rule": 6,
"condition": {
"degree": ["sedang"],
"dominant_lesion": ["papula_pustula"],
"skin_type": ["sensitif", "kering"],
},
"recommendation": {
"active_ingredient": "Azelaic Acid 15-20% (pagi) + Dermokosmetik (malam)",
"product_type": "Treatment",
"dosage": "Pea-size untuk seluruh wajah",
"frequency": "2x sehari (pagi AA, malam dermokosmetik)",
"time_of_day": "pagi_malam",
"instructions": (
"Pagi: Azelaic Acid 15-20% mengurangi inflamasi dan bakteri. "
"Malam: Dermokosmetik multitarget untuk perbaikan sawar kulit. "
"Azelaic Acid aman untuk kulit sensitif dan ibu hamil."
),
"evidence": "1B (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Obat Keras (Resep Dokter) - ASEAN Cosmetic Directive",
"pregnancy_category": "Kategori B (Relatif aman, konsultasi dokter)",
"drug_interactions": [
"HINDARI dengan Retinol/Isotretinoin (meningkatkan iritasi)",
"HINDARI dengan produk astringent atau alkohol kuat",
],
},
"contraindications": [
"AZELAIC ACID: Hanya dengan resep dokter",
"Hindari penggunaan bersamaan dengan Retinol atau Isotretinoin",
],
},
# ── BERAT: Nodul atau Komedo/Papula banyak + Semua tipe kulit ──
# PERDOSKI: Rujuk dokter. Oral Isotretinoin (1B) atau AB (1A)
{
"rule": 7,
"condition": {
"degree": ["berat"],
"dominant_lesion": ["nodul", "papula_pustula", "comedone"],
"skin_type": ["berminyak", "kering", "sensitif", "kombinasi"],
},
"recommendation": {
"active_ingredient": "STOP - Rujuk Dokter Spesialis Kulit",
"product_type": "Konsultasi Medis",
"dosage": "N/A",
"frequency": "Segera",
"time_of_day": "segera",
"instructions": (
"HENTIKAN pengobatan mandiri. Jerawat derajat berat membutuhkan penanganan medis profesional. "
"Kemungkinan memerlukan: Isotretinoin oral (1-2 mg/kg/hari), "
"Antibiotik oral (Doksisiklin 2x100 mg/hari), "
"atau terapi kombinasi sesuai indikasi dokter."
),
"evidence": "1B (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Semua terapi memerlukan resep dokter",
"pregnancy_category": "Isotretinoin: TERATOGENIK (Dilarang saat hamil)",
"drug_interactions": [
"Semua terapi harus diawasi oleh dokter spesialis kulit",
"Isotretinoin: Dilarang saat hamil dan menyusui",
],
},
"contraindications": [
"Semua terapi: HANYA dengan resep dokter spesialis kulit",
"Jangan memencet jerawat nodul",
"Isotretinoin: TERATOGENIK - Dilarang saat hamil",
],
},
# ── SEDANG/RINGAN: Nodul + Semua tipe kulit ──
# PERDOSKI: Nodul = selalu rujuk dokter, terlepas dari derajat lain
{
"rule": 8,
"condition": {
"degree": ["sedang", "ringan"],
"dominant_lesion": ["nodul"],
"skin_type": ["berminyak", "kering", "sensitif", "kombinasi"],
},
"recommendation": {
"active_ingredient": "STOP - Rujuk Dokter Spesialis Kulit",
"product_type": "Konsultasi Medis",
"dosage": "N/A",
"frequency": "Segera",
"time_of_day": "segera",
"instructions": (
"Terdeteksi jerawat nodul yang memerlukan penanganan medis. "
"Nodul adalah bentuk jerawat dalam yang meninggalkan bekas permanen jika tidak ditangani. "
"Segera konsultasi ke dokter spesialis kulit untuk evaluasi dan rencana pengobatan."
),
"evidence": "1B (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Semua terapi memerlukan resep dokter",
"pregnancy_category": "Konsultasi dokter",
"drug_interactions": [
"Semua terapi harus diawasi oleh dokter spesialis kulit",
],
},
"contraindications": [
"Semua terapi: HANYA dengan resep dokter spesialis kulit",
"Jangan memencet jerawat nodul",
],
},
# ── SEDANG: Komedo saja (banyak) + Berminyak/Kombinasi ──
# PERDOSKI: Komedo 20-100 tanpa lesi inflamasi → BHA kuat + Adapalene
{
"rule": 9,
"condition": {
"degree": ["sedang"],
"dominant_lesion": ["comedone"],
"skin_type": ["berminyak", "kombinasi"],
},
"recommendation": {
"active_ingredient": "Salicylic Acid (BHA) 2% + Adapalene 0.1%",
"product_type": "Treatment",
"dosage": "2-3 tetes BHA (pagi), pea-size Adapalene (malam)",
"frequency": "2x sehari (pagi BHA, malam Adapalene)",
"time_of_day": "pagi_malam",
"instructions": (
"Pagi: Salicylic Acid (BHA) 2% untuk eksfoliasi pori. "
"Malam: Adapalene 0.1% mulai 3-4x seminggu, tingkatkan bertahap. "
"Komedo yang banyak memerlukan kombinasi eksfolian + retinoid untuk hasil optimal. "
"Gunakan pelembap sebagai penghalang di antara aplikasi."
),
"evidence": "1A-2B (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Adapalene: Obat Keras (Resep Dokter). BHA: Obat Bebas (OTC).",
"pregnancy_category": "Adapalene: Kategori C (HINDARI saat hamil). BHA: Hati-hati.",
"drug_interactions": [
"JANGAN gunakan BHA dan Adapalene pada waktu yang sama",
"Pagi: BHA saja. Malam: Adapalene saja.",
"Adapalene: JANGAN dicampur dengan Sulfur atau AHA",
],
},
"contraindications": [
"ADAPALENE: Hanya dengan resep dokter",
"Tidak untuk ibu hamil (Adapalene - Kategori C)",
"Jangan aplikasikan BHA dan Adapalene bersamaan",
],
},
# ── SEDANG: Komedo saja (banyak) + Sensitif/Kering ──
# PERDOSKI: Komedo 20-100 tanpa lesi inflamasi → SA lembut + AA
{
"rule": 10,
"condition": {
"degree": ["sedang"],
"dominant_lesion": ["comedone"],
"skin_type": ["sensitif", "kering"],
},
"recommendation": {
"active_ingredient": "Salicylic Acid (BHA) 0.5-1% + Azelaic Acid 15%",
"product_type": "Treatment",
"dosage": "Secukupnya",
"frequency": "2x sehari (pagi AA, malam BHA rendah)",
"time_of_day": "pagi_malam",
"instructions": (
"Pagi: Azelaic Acid 15% untuk anti-inflamasi dan eksfoliasi lembut. "
"Malam: BHA rendah (0.5-1%) untuk membersihkan pori tanpa iritasi. "
"Untuk kulit sensitif, gunakan frekuensi rendah dulu lalu tingkatkan. "
"Pelembap wajib setiap kali setelah treatment."
),
"evidence": "1B-2B (PERDOSKI 2024)",
"prescription_required": True,
"otc_status": "Azelaic Acid: Obat Keras (Resep Dokter). BHA: Obat Bebas (OTC).",
"pregnancy_category": "Azelaic Acid: Kategori B (Relatif aman). BHA: Hati-hati.",
"drug_interactions": [
"JANGAN gunakan BHA dan Adapalene bersamaan",
"Azelaic Acid: HINDARI dengan Retinol/Isotretinoin",
],
},
"contraindications": [
"AZELAIC ACID: Hanya dengan resep dokter",
"Hindari penggunaan bersamaan dengan Retinol atau Isotretinoin",
],
},
]
# ═══════════════════════════════════════════════════════════════════
# LAYER 2: DAILY SKINCARE (Berdasarkan tipe kulit)
# PERDOSKI 2024: Adjuvan - KIE dan SC (1A), tabir surya (1C)
# ═══════════════════════════════════════════════════════════════════
DAILY_SKINCARE = {
"berminyak": {
"facial_wash": {
"name": "Facial Wash Gel/Foam pH Balanced",
"description": "Pembersih berbentuk gel atau busa, pH balanced, bebas alkohol",
"ingredients": "Tea tree, salicylic acid ringan, niacinamide",
"usage": "2x sehari (pagi dan malam)",
},
"moisturizer": {
"name": "Moisturizer Oil-Free / Non-Comedogenic",
"description": "Pelembap ringan tanpa minyak, tidak menyumbat pori",
"ingredients": "Hyaluronic acid, niacinamide, aloe vera",
"usage": "2x sehari setelah treatment",
},
"sunscreen": {
"name": "Sunscreen SPF 30-50 PA+++",
"description": "Pelindung UV ringan, tidak lengket, non-comedogenic",
"ingredients": "SPF 30-50, PA+++, formula gel atau air",
"usage": "Pagi hari, 15 menit sebelum keluar rumah, reapply tiap 2-3 jam",
},
},
"kering": {
"facial_wash": {
"name": "Facial Wash Cream/Milk pH Balanced",
"description": "Pembersih lembut berbentuk krim atau susu, tidak mengikis kelembapan",
"ingredients": "Ceramide, glycerin, centella asiatica",
"usage": "2x sehari (pagi dan malam)",
},
"moisturizer": {
"name": "Moisturizer Hydrating / Ceramide",
"description": "Pelembap kaya nutrisi untuk mengembalikan sawar kulit",
"ingredients": "Ceramide, hyaluronic acid, squalane, shea butter",
"usage": "2x sehari setelah treatment",
},
"sunscreen": {
"name": "Sunscreen SPF 30-50 PA+++ Hydrating",
"description": "Pelindung UV dengan formula melembapkan",
"ingredients": "SPF 30-50, PA+++, dengan hyaluronic acid",
"usage": "Pagi hari, 15 menit sebelum keluar rumah, reapply tiap 2-3 jam",
},
},
"sensitif": {
"facial_wash": {
"name": "Facial Wash Gentle / Fragrance-Free",
"description": "Pembersih sangat lembut, bebas pewangi dan alkohol",
"ingredients": "Centella asiatica, panthenol, allantoin",
"usage": "2x sehari (pagi dan malam)",
},
"moisturizer": {
"name": "Moisturizer Calming / Centella",
"description": "Pelembap menenangkan, mengurangi kemerahan dan iritasi",
"ingredients": "Aloe vera, centella asiatica, madecassoside, panthenol",
"usage": "2x sehari setelah treatment",
},
"sunscreen": {
"name": "Sunscreen Mineral SPF 30-50",
"description": "Pelindung UV minimal iritasi, formula fisik (mineral)",
"ingredients": "SPF 30-50, PA+++, zinc oxide/titanium dioxide",
"usage": "Pagi hari, 15 menit sebelum keluar rumah, reapply tiap 2-3 jam",
},
},
"kombinasi": {
"facial_wash": {
"name": "Facial Wash Gentle Foam pH Balanced",
"description": "Pembersih ringan berbentuk busa lembut",
"ingredients": "Glycerin, niacinamide ringan, centella asiatica",
"usage": "2x sehari (pagi dan malam)",
},
"moisturizer": {
"name": "Moisturizer Gel-Cream Oil-Free",
"description": "Pelembap bertekstur gel-cream, ringan tapi melembapkan",
"ingredients": "Hyaluronic acid, niacinamide, aloe vera",
"usage": "2x sehari setelah treatment",
},
"sunscreen": {
"name": "Sunscreen SPF 30-50 PA+++ Gel",
"description": "Pelindung UV ringan, tidak lengket di T-zone",
"ingredients": "SPF 30-50, PA+++, formula gel atau air",
"usage": "Pagi hari, 15 menit sebelum keluar rumah, reapply tiap 2-3 jam",
},
},
}
# ═══════════════════════════════════════════════════════════════════
# FUNGSI EVALUASI FORWARD CHAINING
# ═══════════════════════════════════════════════════════════════════
def _get_dominant_lesion(summary: dict) -> str:
"""Menentukan jenis lesi dominan dari summary deteksi.
Note: Nodul TIDAK dijadikan dominant lesion karena:
- Penanganan nodul selalu rujuk dokter (tidak ada self-treatment)
- Kita tetap ingin match rule untuk lesi LAIN yang bisa ditreatment
- Nodul ditangani terpisah via nodul_alert
"""
if not summary:
return "comedone"
comedone_count = summary.get("comedone", 0)
papula_count = summary.get("papules", 0)
pustule_count = summary.get("pustules", 0)
papula_pustula_total = papula_count + pustule_count
if papula_pustula_total > comedone_count:
return "papula_pustula"
return "comedone"
def _has_nodul(summary: dict) -> bool:
"""Cek apakah ada nodul dalam deteksi."""
return summary.get("nodules", 0) > 0
def _evaluate_rules(degree: str, dominant_lesion: str, skin_type: str) -> list:
"""Evaluasi semua rules dan kembalikan rules yang cocok."""
matched_rules = []
for rule in RULES:
cond = rule["condition"]
if degree not in cond["degree"]:
continue
if dominant_lesion not in cond["dominant_lesion"]:
continue
if skin_type not in cond["skin_type"]:
continue
matched_rules.append(rule)
return matched_rules
def get_expert_recommendation(
detected_classes: list,
summary: dict,
skin_type: str,
severity_level: str,
) -> dict:
"""
Forward Chaining: evaluasi rules dan kembalikan rekomendasi.
Berdasarkan PERDOSKI 2024:
- Ringan: Topikal lini 1 (SA/RA/BPO)
- Sedang: Topikal lini 1 + Oral (Dox)
- Berat: Rujuk dokter, oral Isotretinoin/AB
Returns:
dict berisi:
- expert_rule: info rule yang aktif
- recommendation: rekomendasi treatment (Layer 1)
- daily_skincare: rekomendasi daily skincare (Layer 2)
- nonmedikamentosa: tips non obat (PERDOSKI 2024)
- maintenance: tips pemeliharaan (PERDOSKI 2024)
- active_ingredient_info: info legalitas bahan aktif
- nodul_alert: peringatan nodul (jika ada nodul terdeteksi)
"""
if skin_type not in VALID_SKIN_TYPES:
skin_type = "berminyak"
dominant_lesion = _get_dominant_lesion(summary)
# ── Jika ada nodul, hitung severity TANPA nodul untuk treatment lesi lain ──
has_nodul = _has_nodul(summary)
if has_nodul:
# Buat summary tanpa nodul
non_nodul_summary = {k: v for k, v in summary.items() if k != "nodules"}
non_nodul_score = sum(
count * {"comedone": 1, "papules": 2, "pustules": 2}.get(cls, 1)
for cls, count in non_nodul_summary.items()
)
# Map skor non-nodul ke severity
if non_nodul_score == 0:
non_nodul_severity = "clear"
elif non_nodul_score <= 9:
non_nodul_severity = "mild"
elif non_nodul_score <= 19:
non_nodul_severity = "moderate"
else:
non_nodul_severity = "severe"
treatment_degree = SEVERITY_TO_DEGREE.get(non_nodul_severity, "ringan")
else:
treatment_degree = SEVERITY_TO_DEGREE.get(severity_level, "ringan")
matched_rules = _evaluate_rules(treatment_degree, dominant_lesion, skin_type)
if matched_rules:
active_rule = matched_rules[0]
recommendation = active_rule["recommendation"]
contraindications = active_rule.get("contraindications", [])
rule_number = active_rule["rule"]
else:
recommendation = {
"active_ingredient": "Konsultasi Dokter Kulit",
"product_type": "Konsultasi Medis",
"dosage": "N/A",
"frequency": "Segera",
"time_of_day": "segera",
"instructions": "Tidak ada rekomendasi spesifik. Silakan konsultasi dengan dokter kulit.",
"evidence": "-",
"prescription_required": True,
"otc_status": "Konsultasi dokter",
"pregnancy_category": "Konsultasi dokter",
"drug_interactions": [],
}
contraindications = []
rule_number = 0
daily_skincare = DAILY_SKINCARE.get(skin_type, DAILY_SKINCARE["berminyak"])
# Nonmedikamentosa (PERDOSKI 2024)
nonmedikamentosa = [
"Cuci muka 2x sehari dengan sabun pH balanced, bebas alkohol, non-abrasif",
"Hindari penggunaan kosmetik yang bersifat oklusif",
"Batasi konsumsi makanan indeks glikemik tinggi (karbohidrat, makanan berlemak, produk olahan susu)",
"Identifikasi dan kelola stres psikologis",
]
# Maintenance (PERDOSKI 2024)
maintenance = [
"Setelah remisi, lanjutkan retinoid topikal <0.01-0.025% untuk pencegahan kekambuhan",
"Gunakan sabun wajah, pelembap, dan tabir surya sesuai kondisi kulit",
"Evaluasi respons pengobatan tiap 8-12 minggu",
]
# Info bahan aktif berdasarkan rekomendasi
active_ingredient_info = _get_active_ingredient_info(recommendation["active_ingredient"])
# Nodul alert: selalu tampil jika nodul terdeteksi (terlepas dari rule)
nodul_alert = None
if _has_nodul(summary):
nodule_count = summary.get("nodules", 0)
nodul_alert = {
"detected": True,
"count": nodule_count,
"title": "Nodul Terdeteksi",
"message": (
f"Terdeteksi {nodule_count} nodul pada wajah. "
"Nodul adalah jerawat dalam yang tidak dapat ditangani dengan skincare atau obat bebas. "
"Diperlukan penanganan medis oleh dokter spesialis kulit."
),
"treatments": [
"Injeksi triamsinolon acetonide (TAIL) intralesi — dosis 0,03 mg per lesi",
"Ekstraksi lesi oleh dokter",
"Isotretinoin oral (untuk kasus berat) — hanya dengan resep dokter",
],
"urgency": "Segera — risiko bekas permanen jika tidak ditangani",
}
return {
"expert_rule": {
"rule_number": rule_number,
"degree": treatment_degree,
"dominant_lesion": dominant_lesion,
"skin_type": skin_type,
"input_summary": {
"severity_level": severity_level,
"detected_classes": detected_classes,
"lesion_counts": summary,
"has_nodul": has_nodul,
},
},
"recommendation": {
**recommendation,
"contraindications": contraindications,
},
"daily_skincare": daily_skincare,
"nonmedikamentosa": nonmedikamentosa,
"maintenance": maintenance,
"active_ingredient_info": active_ingredient_info,
"nodul_alert": nodul_alert,
}
def _get_active_ingredient_info(ingredient_str: str) -> list:
"""Ambil info bahan aktif berdasarkan string rekomendasi."""
info = []
ingredient_lower = ingredient_str.lower()
if "salicylic" in ingredient_lower or "bha" in ingredient_lower:
info.append(ACTIVE_INGREDIENT_INFO["salicylic_acid"])
if "adapalene" in ingredient_lower:
info.append(ACTIVE_INGREDIENT_INFO["adapalene"])
if "benzoyl" in ingredient_lower or "bpo" in ingredient_lower:
info.append(ACTIVE_INGREDIENT_INFO["benzoyl_peroxide"])
if "azelaic" in ingredient_lower:
info.append(ACTIVE_INGREDIENT_INFO["azelaic_acid"])
if "niacinamide" in ingredient_lower:
info.append(ACTIVE_INGREDIENT_INFO["niacinamide"])
return info