File size: 5,584 Bytes
6aa907d
 
c3fc7ce
6aa907d
 
 
 
 
 
b1d1fcb
6aa907d
b1d1fcb
6aa907d
 
 
 
 
199d49b
6aa907d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199d49b
6aa907d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# GoodFind β€” OCR (+ optional ML) with graceful fallback
import streamlit as st, base64, shutil

# Optional ML (if you added valuation.py earlier)
try:
    from valuation import DealValuator, deal_score
    HAS_ML = True
except Exception:
    HAS_ML = False

from ocr_utils import ocr_image, guess_price, guess_title, annotate_price_box

st.set_page_config(page_title="GoodFind (OCR Demo)", page_icon="πŸ›’")
st.title("πŸ›’ GoodFind β€” OCR Demo")

# Check if the tesseract binary exists (fallback-safe)
HAS_TESSERACT = shutil.which("tesseract") is not None

status = []
status.append("OCR engine: βœ… available" if HAS_TESSERACT else "OCR engine: ❌ missing (fallback to manual)")
status.append("ML estimator: βœ…" if HAS_ML else "ML estimator: ❌ (using keyword fallback)")
st.caption(" Β· ".join(status))

# If ML is present, initialize once
if HAS_ML and 'valuator' not in st.session_state:
    st.session_state['valuator'] = DealValuator()
valuator = st.session_state.get('valuator')

# Simple fallback estimator (if no ML)
PRICE_TABLE = {"wii":85,"playstation":120,"ps2":75,"ps3":110,"ps4":160,"ps5":350,"iphone":240,"macbook":450,"aeron":500,
               "pyrex":120,"le creuset":240,"kitchenaid":220,"bose":140,"walkman":95,"marantz":400,"yeti":18,"dansko":40,
               "coach":65,"levi":28,"seiko":95,"all-clad":55}
DEFAULT_RESALE = 45.0
def quick_estimate(title: str) -> float:
    t = (title or "").lower()
    best = None
    for k, v in PRICE_TABLE.items():
        if k in t:
            best = max(best or 0, v)
    return float(best if best else DEFAULT_RESALE)

def deal_score_simple(predicted_resale: float, ask: float, fee_rate: float=0.13, ship: float=12.0):
    fees = predicted_resale * fee_rate
    net = predicted_resale - fees - ship
    profit = net - ask
    margin = (profit / ask) if ask > 0 else 0.0
    if profit >= 50 and margin >= 0.8: label = "Home Run"
    elif profit >= 25 and margin >= 0.5: label = "Great"
    elif profit >= 10 and margin >= 0.3: label = "Good"
    elif profit >= 5: label = "Meh"
    else: label = "Pass"
    return round(fees,2), round(net,2), round(profit,2), round(margin,2), label

# UI controls
fees_rate = st.slider("Fees rate", 0.05, 0.20, 0.13, 0.01)
ship = st.slider("Shipping estimate ($)", 0.0, 30.0, 12.0, 1.0)
autoscan = st.checkbox("Auto-scan photos with OCR (read price tags + suggest names)", value=True)

photos = st.file_uploader("Upload photos (JPG/PNG/WebP)", type=["jpg","jpeg","png","webp"], accept_multiple_files=True)
items = []

if photos:
    for i, img in enumerate(photos):
        raw = img.read()
        suggested_title, suggested_price, annotated = "", "", None

        if autoscan and HAS_TESSERACT:
            text, tokens, _ = ocr_image(raw)
            suggested_title = guess_title(text) or ""
            gp = guess_price(tokens)
            if gp:
                val, box = gp
                suggested_price = f"{val:.2f}"
                try:
                    annotated = annotate_price_box(raw, box, label=f"${val:.2f}")
                except Exception:
                    annotated = None
        elif autoscan and not HAS_TESSERACT:
            st.info("OCR isn’t available in this runtime; you can still type the name and price manually.")

        with st.expander(f"Photo {i+1}", expanded=True):
            st.image(annotated or raw, use_container_width=True)
            c1, c2 = st.columns([2,1])
            with c1:
                name = st.text_input(f"Item name #{i+1}", value=suggested_title, placeholder="e.g., Bose QC35 headphones", key=f"name_{i}")
            with c2:
                price_txt = st.text_input(f"Asking price #{i+1} ($)", value=suggested_price, placeholder="e.g., 25.00", key=f"price_{i}")
        items.append((name, price_txt, raw, img.type or "image/jpeg"))

if st.button("Evaluate group", type="primary"):
    rows, gallery_blocks = [], []
    for idx, (name, price_txt, raw, mime) in enumerate(items):
        name = (name or "").strip() or f"Item {idx+1}"
        try:
            ask = float(price_txt) if price_txt and price_txt.strip() else 0.0
        except:
            ask = 0.0

        if HAS_ML and valuator:
            resale = valuator.predict_resale(name)
            fees_v, net, profit, margin, label = deal_score(resale, ask, fees_rate, ship)
        else:
            resale = quick_estimate(name)
            fees_v, net, profit, margin, label = deal_score_simple(resale, ask, fees_rate, ship)

        rows.append({"title": name, "ask": ask, "resale": resale, "profit": profit, "margin": margin, "label": label})

        b64 = base64.b64encode(raw).decode("utf-8")
        gallery_blocks.append(f"""
            <div class="img-wrap" title="{name}">
              <img src="data:{mime};base64,{b64}" alt="{name}"/>
              <span class="hover-label">{name}</span>
            </div>
        """)

    if rows:
        rows.sort(key=lambda r: (r["profit"], r["resale"]), reverse=True)
        st.markdown("### Ranked results")
        st.table([
            {"Title": r["title"], "Ask ($)": f"{r['ask']:.2f}",
             "Est. resale ($)": f"{r['resale']:.2f}", "Profit ($)": f"{r['profit']:.2f}",
             "Margin": f"{r['margin']:.2f}", "Verdict": r["label"]}
            for r in rows
        ])
        top = rows[0]
        st.success(f"Top pick: **{top['title']}** β€” resale ${top['resale']:.0f}, profit ${top['profit']:.0f} ({top['label']})")

        st.markdown("### Hover over each image to see the item name")
        st.markdown("".join(gallery_blocks), unsafe_allow_html=True)