|
|
|
|
|
|
|
|
|
|
|
import gradio as gr |
|
|
import joblib |
|
|
import re |
|
|
import pandas as pd |
|
|
from datetime import datetime |
|
|
|
|
|
|
|
|
try: |
|
|
model = joblib.load('halal_classifier_model.pkl') |
|
|
except FileNotFoundError: |
|
|
print("Error: 'halal_classifier_model.pkl' not found.") |
|
|
model = None |
|
|
|
|
|
|
|
|
comprehensive_ingredients_db = { |
|
|
|
|
|
|
|
|
|
|
|
'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.'}, |
|
|
|
|
|
|
|
|
|
|
|
'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' |
|
|
|
|
|
|
|
|
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] |
|
|
|
|
|
|
|
|
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.") |
|
|
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}" |
|
|
|
|
|
|
|
|
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 | Tareq Al-Omari | Abdulrahman Sinan</strong></p> |
|
|
</div> |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
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] |
|
|
) |
|
|
|
|
|
|
|
|
iface.launch() |