|
|
import streamlit as st
|
|
|
import requests
|
|
|
import json
|
|
|
from PIL import Image
|
|
|
import io
|
|
|
import base64
|
|
|
from datetime import datetime
|
|
|
import pandas as pd
|
|
|
import plotly.express as px
|
|
|
|
|
|
|
|
|
st.set_page_config(
|
|
|
page_title="IndiScan - Product Health Analyzer",
|
|
|
page_icon="π",
|
|
|
layout="wide"
|
|
|
)
|
|
|
|
|
|
|
|
|
API_URL = "http://localhost:8000"
|
|
|
|
|
|
def main():
|
|
|
|
|
|
st.sidebar.title("IndiScan π")
|
|
|
scan_option = st.sidebar.radio(
|
|
|
"Choose scan method:",
|
|
|
["Barcode", "Image Upload", "Manual Entry"]
|
|
|
)
|
|
|
|
|
|
|
|
|
st.title("IndiScan - Product Health Analyzer")
|
|
|
st.markdown("""
|
|
|
Analyze food and cosmetic products for health risks and get detailed insights.
|
|
|
Upload an image, enter a barcode, or manually input product details.
|
|
|
""")
|
|
|
|
|
|
|
|
|
with st.sidebar.expander("Admin Controls π"):
|
|
|
admin_username = st.text_input("Username")
|
|
|
admin_password = st.text_input("Password", type="password")
|
|
|
if st.button("Login"):
|
|
|
try:
|
|
|
auth = (admin_username, admin_password)
|
|
|
response = requests.get(f"{API_URL}/export", auth=auth)
|
|
|
if response.status_code == 200:
|
|
|
st.sidebar.success("Logged in as admin")
|
|
|
st.session_state['admin_auth'] = auth
|
|
|
else:
|
|
|
st.sidebar.error("Invalid credentials")
|
|
|
except Exception as e:
|
|
|
st.sidebar.error(f"Login failed: {str(e)}")
|
|
|
|
|
|
|
|
|
if scan_option == "Barcode":
|
|
|
barcode_scanner()
|
|
|
elif scan_option == "Image Upload":
|
|
|
image_scanner()
|
|
|
else:
|
|
|
manual_entry()
|
|
|
|
|
|
def barcode_scanner():
|
|
|
st.header("Barcode Scanner π±")
|
|
|
barcode = st.text_input("Enter barcode number:")
|
|
|
|
|
|
if barcode:
|
|
|
try:
|
|
|
response = requests.post(f"{API_URL}/scan/barcode", params={"barcode": barcode})
|
|
|
if response.status_code == 200:
|
|
|
display_results(response.json())
|
|
|
else:
|
|
|
st.error("Product not found")
|
|
|
except Exception as e:
|
|
|
st.error(f"Error: {str(e)}")
|
|
|
|
|
|
def image_scanner():
|
|
|
st.header("Image Scanner πΈ")
|
|
|
uploaded_file = st.file_uploader("Upload product image", type=["jpg", "jpeg", "png"])
|
|
|
|
|
|
if uploaded_file:
|
|
|
try:
|
|
|
|
|
|
image = Image.open(uploaded_file)
|
|
|
st.image(image, caption="Uploaded Image", use_column_width=True)
|
|
|
|
|
|
|
|
|
files = {"file": uploaded_file}
|
|
|
response = requests.post(f"{API_URL}/scan/image", files=files)
|
|
|
|
|
|
if response.status_code == 200:
|
|
|
display_results(response.json())
|
|
|
else:
|
|
|
st.error("Failed to process image")
|
|
|
except Exception as e:
|
|
|
st.error(f"Error: {str(e)}")
|
|
|
|
|
|
def manual_entry():
|
|
|
st.header("Manual Entry βοΈ")
|
|
|
|
|
|
col1, col2 = st.columns(2)
|
|
|
|
|
|
with col1:
|
|
|
product_type = st.selectbox("Product Type", ["Food", "Cosmetic"])
|
|
|
ingredients_text = st.text_area("Enter ingredients list (comma-separated or as shown on package):")
|
|
|
|
|
|
with col2:
|
|
|
if ingredients_text:
|
|
|
try:
|
|
|
response = requests.post(
|
|
|
f"{API_URL}/analyze/text",
|
|
|
data={
|
|
|
"text": ingredients_text,
|
|
|
"product_type": product_type.lower()
|
|
|
}
|
|
|
)
|
|
|
|
|
|
if response.status_code == 200:
|
|
|
display_results(response.json())
|
|
|
else:
|
|
|
st.error("Failed to analyze ingredients")
|
|
|
except Exception as e:
|
|
|
st.error(f"Error: {str(e)}")
|
|
|
|
|
|
def display_results(data):
|
|
|
|
|
|
col1, col2, col3 = st.columns([2, 2, 1])
|
|
|
|
|
|
with col1:
|
|
|
st.subheader("Health Score")
|
|
|
score = data.get('health_score', {}).get('score', 0)
|
|
|
|
|
|
|
|
|
fig = px.pie(
|
|
|
values=[score, 1000-score],
|
|
|
names=['Score', 'Remaining'],
|
|
|
hole=0.7,
|
|
|
color_discrete_sequence=['#00ff00' if score > 600 else '#ff0000', '#eee']
|
|
|
)
|
|
|
fig.update_layout(
|
|
|
annotations=[dict(text=f"{score}/1000", x=0.5, y=0.5, font_size=20, showarrow=False)],
|
|
|
showlegend=False,
|
|
|
width=300,
|
|
|
height=300
|
|
|
)
|
|
|
st.plotly_chart(fig)
|
|
|
|
|
|
|
|
|
if 'explanation' in data.get('health_score', {}):
|
|
|
st.markdown(data['health_score']['explanation'])
|
|
|
|
|
|
with col2:
|
|
|
st.subheader("Ingredients Analysis")
|
|
|
if 'ingredients' in data:
|
|
|
ingredients = data['ingredients']
|
|
|
st.write(f"Found {len(ingredients)} ingredients:")
|
|
|
for i, ingredient in enumerate(ingredients, 1):
|
|
|
st.write(f"{i}. {ingredient}")
|
|
|
|
|
|
|
|
|
if 'risks' in data.get('health_score', {}):
|
|
|
st.subheader("Risk Categories")
|
|
|
risks = data['health_score']['risks']
|
|
|
for category, risk_data in risks.items():
|
|
|
with st.expander(f"{category.replace('_', ' ').title()}"):
|
|
|
st.write(f"Found in: {', '.join(risk_data['ingredients'])}")
|
|
|
|
|
|
with col3:
|
|
|
if 'nutrition_info' in data:
|
|
|
st.subheader("Nutrition Info")
|
|
|
nutrition = data['nutrition_info']
|
|
|
for nutrient, value in nutrition.items():
|
|
|
st.metric(nutrient.title(), f"{value}g")
|
|
|
|
|
|
if 'nutrition_analysis' in data:
|
|
|
analysis = data['nutrition_analysis']
|
|
|
|
|
|
if analysis['concerns']:
|
|
|
st.subheader("β οΈ Concerns")
|
|
|
for concern in analysis['concerns']:
|
|
|
st.write(f"- {concern}")
|
|
|
|
|
|
if analysis['positives']:
|
|
|
st.subheader("β
Positives")
|
|
|
for positive in analysis['positives']:
|
|
|
st.write(f"- {positive}")
|
|
|
|
|
|
if analysis['recommendations']:
|
|
|
st.subheader("π‘ Recommendations")
|
|
|
for rec in analysis['recommendations']:
|
|
|
st.write(f"- {rec}")
|
|
|
|
|
|
|
|
|
if 'prices' in data:
|
|
|
st.subheader("Price Comparison")
|
|
|
prices_df = pd.DataFrame(data['prices'])
|
|
|
fig = px.bar(
|
|
|
prices_df,
|
|
|
x='platform',
|
|
|
y='price',
|
|
|
title="Price Comparison Across Platforms",
|
|
|
color='platform'
|
|
|
)
|
|
|
st.plotly_chart(fig)
|
|
|
|
|
|
|
|
|
st.dataframe(
|
|
|
prices_df[['platform', 'price', 'title', 'url']],
|
|
|
column_config={
|
|
|
"url": st.column_config.LinkColumn("Link")
|
|
|
}
|
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
main() |