alomari7's picture
Update app.py
109193e verified
# =============================================================================
# Final Code for the Professional Interface (English Version)
# =============================================================================
import gradio as gr
import joblib
import re
import pandas as pd
from datetime import datetime
# --- 1. Load Model and Knowledge Base ---
try:
model = joblib.load('halal_classifier_model.pkl')
except FileNotFoundError:
print("Error: 'halal_classifier_model.pkl' not found.")
model = None
# --- Use your complete knowledge base here ---
comprehensive_ingredients_db = {
#==========================================================================
# ุงู„ู‚ุณู… ุงู„ุฃูˆู„: ู…ูƒูˆู†ุงุช ู…ุญุฑู…ุฉ ุจุดูƒู„ ู‚ุงุทุน (Haram)
#==========================================================================
'pork': {'status': 'Haram', 'reason': 'Pork is explicitly forbidden in the Quran.'},
'bacon': {'status': 'Haram', 'reason': 'A type of cured pork.'},
'ham': {'status': 'Haram', 'reason': 'Meat from the thigh of a pig.'},
'lard': {'status': 'Haram', 'reason': 'Fat from the abdomen of a pig.'},
'swine': {'status': 'Haram', 'reason': 'Another name for a pig.'},
'hog': {'status': 'Haram', 'reason': 'Another name for a pig.'},
'boar': {'status': 'Haram', 'reason': 'A wild pig.'},
'porcine': {'status': 'Haram', 'reason': 'Derived from pigs.'},
'speck': {'status': 'Haram', 'reason': 'A type of cured pork belly.'},
'schwein': {'status': 'Haram', 'reason': "German word for 'pig'."},
'prosciutto': {'status': 'Haram', 'reason': 'Italian dry-cured ham.'},
'alcohol': {'status': 'Haram', 'reason': 'All intoxicants are forbidden.'},
'ethanol': {'status': 'Haram', 'reason': 'The chemical name for intoxicating alcohol.'},
'ethyl alcohol': {'status': 'Haram', 'reason': 'The chemical name for intoxicating alcohol.'},
'wine': {'status': 'Haram', 'reason': 'Wine is explicitly forbidden.'},
'beer': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'liquor': {'status': 'Haram', 'reason': 'A general term for distilled alcoholic drinks.'},
'liqueur': {'status': 'Haram', 'reason': 'A sweetened alcoholic beverage.'},
'brandy': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'cognac': {'status': 'Haram', 'reason': 'A type of brandy, which is an intoxicant.'},
'gin': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'rum': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'vodka': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'whisky': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'whiskey': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'champagne': {'status': 'Haram', 'reason': 'A sparkling wine, which is an intoxicant.'},
'sake': {'status': 'Haram', 'reason': 'Japanese rice wine, which is an intoxicant.'},
'vermouth': {'status': 'Haram', 'reason': 'A fortified and flavored wine, which is an intoxicant.'},
'sherry': {'status': 'Haram', 'reason': 'A type of fortified wine, which is an intoxicant.'},
'port': {'status': 'Haram', 'reason': 'A type of fortified wine, which is an intoxicant.'},
'absinthe': {'status': 'Haram', 'reason': 'An intoxicating alcoholic beverage.'},
'blood': {'status': 'Haram', 'reason': 'Flowing blood is explicitly forbidden.'},
'carrion': {'status': 'Haram', 'reason': 'Meat from an animal that was not slaughtered according to Islamic law.'},
'carmine': {'status': 'Haram', 'reason': 'A red pigment (E120) derived from insects, considered forbidden by the majority of scholars.'},
'cochineal': {'status': 'Haram', 'reason': 'Another name for carmine dye (E120) from insects.'},
'e120': {'status': 'Haram', 'reason': 'The E-number for carmine dye from insects.'},
'e-120': {'status': 'Haram', 'reason': 'The E-number for carmine dye from insects.'},
'ci 75470': {'status': 'Haram', 'reason': 'The international color index for carmine dye from insects.'},
#==========================================================================
# ุงู„ู‚ุณู… ุงู„ุซุงู†ูŠ: ู…ูƒูˆู†ุงุช ู…ุดุจูˆู‡ุฉ (Doubtful / Mashbooh)
#==========================================================================
'gelatin': {'status': 'Doubtful (leaning towards Haram)', 'reason': 'Its source is mostly animal (pork or not slaughtered correctly). Requires a reliable Halal certificate.'},
'e441': {'status': 'Doubtful (leaning towards Haram)', 'reason': 'Old E-number for Gelatin. Its source is often animal-based (pork or not slaughtered correctly).'},
'e-441': {'status': 'Doubtful (leaning towards Haram)', 'reason': 'Old E-number for Gelatin. Its source is often animal-based (pork or not slaughtered correctly).'},
'animal fat': {'status': 'Doubtful', 'reason': 'Animal fat. Must be from a Halal-slaughtered animal.'},
'animal shortening': {'status': 'Doubtful', 'reason': 'Animal shortening. Must be from a Halal-slaughtered animal.'},
'fat': {'status': 'Doubtful', 'reason': 'A general term for fat. If the source is not specified as vegetable, it is doubtful.'},
'tallow': {'status': 'Doubtful', 'reason': 'Animal fat. Must be confirmed to be from a Halal source.'},
'shortening': {'status': 'Doubtful', 'reason': 'An industrial fat that may contain animal fats. The source must be verified.'},
'margarine': {'status': 'Doubtful', 'reason': 'May contain animal fats or doubtful emulsifiers.'},
'glycerin': {'status': 'Doubtful', 'reason': 'Can be of animal or vegetable origin. The vegetable source must be confirmed.'},
'glycerine': {'status': 'Doubtful', 'reason': 'Another name for Glycerin; can be of animal or vegetable origin.'},
'glycerol': {'status': 'Doubtful', 'reason': 'Another name for Glycerin (E422); can be of animal or vegetable origin.'},
'e422': {'status': 'Doubtful', 'reason': 'E-number for Glycerin/Glycerol. Can be of animal or vegetable origin.'},
'e-422': {'status': 'Doubtful', 'reason': 'E-number for Glycerin/Glycerol. Can be of animal or vegetable origin.'},
'mono and diglycerides': {'status': 'Doubtful', 'reason': 'Emulsifiers (E471) that can be from animal or vegetable fat. Must be verified.'},
'emulsifier': {'status': 'Doubtful', 'reason': 'A general term for emulsifiers. Many are of doubtful (animal) origin.'},
'whey': {'status': 'Doubtful', 'reason': 'Its ruling depends on the rennet used in cheese making.'},
'rennet': {'status': 'Doubtful', 'reason': 'Cheese rennet, which can be from an animal not slaughtered according to Islamic law.'},
'pepsin': {'status': 'Doubtful', 'reason': 'An enzyme often extracted from pig stomachs. Must be verified to be from a plant or microbial source.'},
}
FEEDBACK_FILE = 'feedback_log_en.csv'
# --- 2. Define Backend Functions (Now in English) ---
def predict_and_explain(text_input):
if not model or not text_input.strip():
return "", "", gr.update(visible=False)
prediction = model.predict([text_input])[0]
# Translate prediction for consistency if your model outputs Arabic
status_map = {
"ุญุฑุงู…": "Haram",
"ุญู„ุงู„": "Halal",
"ู…ุดุชุจู‡ ููŠู‡": "Doubtful",
"ู…ุดุชุจู‡ ููŠู‡ (ูŠู…ูŠู„ ู„ู„ุชุญุฑูŠู…)": "Doubtful (leaning towards Haram)"
}
prediction_en = status_map.get(prediction, "Doubtful")
reason = "No direct reason found in the knowledge base."
if prediction_en != 'Halal':
for ingredient, details in comprehensive_ingredients_db.items():
if re.search(r'\b' + re.escape(ingredient) + r'\b', text_input, re.IGNORECASE):
reason = details.get('reason', "Reason not specified.") # Use .get for safety
break
else:
reason = "No clear forbidden or doubtful ingredients were found based on the current database."
return prediction_en, reason, gr.update(visible=True)
def handle_feedback(original_text, model_prediction, user_correction, user_comment):
try:
feedback_data = pd.DataFrame({
'timestamp': [datetime.now()],
'original_text': [original_text],
'model_prediction': [model_prediction],
'user_correction': [user_correction],
'user_comment': [user_comment]
})
try:
pd.read_csv(FEEDBACK_FILE)
write_header = False
except FileNotFoundError:
write_header = True
feedback_data.to_csv(FEEDBACK_FILE, mode='a', header=write_header, index=False, encoding='utf-8-sig')
return "โœ… Thank you for your contribution! Your feedback has been saved."
except Exception as e:
return f"โŒ An error occurred while saving feedback: {e}"
# --- 3. Build the Themed Interface with gr.Blocks ---
theme = gr.themes.Soft(
primary_hue="blue",
secondary_hue="blue",
).set(
button_primary_background_fill='*primary_500',
button_primary_background_fill_hover='*primary_600',
)
with gr.Blocks(theme=theme) as iface:
gr.Markdown(
"""
<div style="text-align: center; font-family: 'Arial', sans-serif; padding-bottom: 15px;">
<h1 style="color: #2c3e50;">Yaqeen</h1>
<p style="font-size: 1.2em; color: #34495e;">
An intelligent system to classify food products by analyzing their ingredients.
</p>
</div>
"""
)
with gr.Row():
with gr.Column(scale=2):
text_input = gr.Textbox(lines=8, label="Enter Product Name & Ingredients", placeholder="e.g., Milk Chocolate, Ingredients: Sugar, cocoa butter, milk powder, soy lecithin E322, vanilla flavor...")
submit_btn = gr.Button("๐Ÿ” Analyze Product", variant="primary")
with gr.Column(scale=3):
status_output = gr.Textbox(label="Predicted Status", interactive=False)
reason_output = gr.Textbox(label="Potential Reason", interactive=False)
with gr.Group(visible=False) as feedback_box:
gr.Markdown("---")
gr.Markdown("<h3 style='text-align:center;'>Was this prediction correct?</h3>")
with gr.Row():
correction_dropdown = gr.Dropdown(
choices=["Halal", "Haram", "Doubtful", "Doubtful (leaning towards Haram)"],
label="If not, what is the correct status?"
)
comment_box = gr.Textbox(label="Additional Comments (Optional)", placeholder="e.g., This product contains Halal beef gelatin...")
feedback_btn = gr.Button("๐Ÿ“ฉ Submit Feedback")
feedback_status = gr.Textbox(label="Submission Status", interactive=False)
gr.Examples(
examples=[
["Prosciutto crudo, E252"],
["Cheddar cheese, water, salt, rennet, whey"],
["Gummy candy with sugar, corn syrup, gelatin, citric acid, carmine color"],
],
inputs=text_input,
label="Examples"
)
gr.Markdown(
"""
<hr>
<div style='text-align: center; font-size: 0.9em; color: #7f8c8d;'>
<p>Developed by Engineers</p>
<p><strong>Ali Al-Qawas &nbsp;|&nbsp; Tareq Al-Omari &nbsp;|&nbsp; Abdulrahman Sinan</strong></p>
</div>
"""
)
# --- Link Functions to Components ---
submit_btn.click(
fn=predict_and_explain,
inputs=[text_input],
outputs=[status_output, reason_output, feedback_box]
)
feedback_btn.click(
fn=handle_feedback,
inputs=[text_input, status_output, correction_dropdown, comment_box],
outputs=[feedback_status]
)
# --- 4. Launch the Interface ---
iface.launch()