import streamlit as st from core import bidder_processor, evaluator from core.config import BIDDER_NAMES, DATA_DIR from core.fallback import load_criteria from core.schemas import Criterion from ui.components import category_badge, confidence_bar, mandatory_badge, ocr_tier_badge, verdict_pill _BIDDER_META = { "bidder_a": ("Apex Constructions Pvt. Ltd.", "Clearly Eligible", "#22C55E"), "bidder_b": ("BuildRight Enterprises", "Ineligible — Low Turnover", "#EF4444"), "bidder_c": ("Shree Constructions & Services", "Needs Review — Scanned Cert", "#F59E0B"), } def _get_criteria() -> list[Criterion]: data = st.session_state.get("criteria") return [Criterion(**c) for c in data] if data else load_criteria() def _overall(verdicts: list[dict], crit_map: dict) -> str: mand = [v for v in verdicts if crit_map.get(v["criterion_id"]) and crit_map[v["criterion_id"]].mandatory] src = mand or verdicts if any(v["verdict"] == "not_eligible" for v in src): return "not_eligible" if any(v["verdict"] == "needs_review" for v in src): return "needs_review" return "eligible" def render() -> None: st.markdown( '

Bidder Evaluation

' '

' 'Evaluate each bidder against all extracted criteria.

', unsafe_allow_html=True, ) selected = st.multiselect( "Select bidders", options=list(BIDDER_NAMES.keys()), default=list(BIDDER_NAMES.keys()), format_func=lambda x: _BIDDER_META.get(x, (x, "", ""))[0], ) criteria_loaded = bool(st.session_state.get("criteria")) if not criteria_loaded: st.info( "**Criteria not loaded yet.** Go to **Tender Analysis** and click " "**Extract Criteria**, or use **Load Pre-computed Demo** on the Overview tab." ) if st.button("▶ Run Evaluation", type="primary", disabled=not criteria_loaded): criteria = _get_criteria() prog = st.progress(0, text="Starting…") total = len(selected) * len(criteria) done, vd = 0, {} for bid in selected: files = sorted(f for f in (DATA_DIR / "bidders" / bid).iterdir() if f.suffix.lower() in {".pdf", ".png", ".jpg"}) with st.spinner(f"Indexing {_BIDDER_META.get(bid,(bid,'',''))[0]}…"): bidder_processor.process_bidder(bid, files) vlist = [] for c in criteria: v = evaluator.evaluate(bid, c) vlist.append(v.model_dump()) done += 1 prog.progress(done / total, text=f"{c.id} · {_BIDDER_META.get(bid,(bid,'',''))[0]}") vd[bid] = vlist st.session_state["verdicts"] = vd prog.empty() st.success("Evaluation complete.") st.rerun() vdata = st.session_state.get("verdicts", {}) criteria = _get_criteria() crit_map = {c.id: c for c in criteria} if not vdata: st.info("No results yet — click **Run Evaluation** above, or load the demo from Overview.") return if st.session_state.get("fallback_active"): st.warning("⚠ Live API unavailable — showing pre-computed results.") for bid in (selected or list(vdata.keys())): if bid not in vdata: continue verdicts = vdata[bid] name, tagline, accent = _BIDDER_META.get(bid, (bid, "", "#3B82F6")) ov = _overall(verdicts, crit_map) passed = sum(1 for v in verdicts if v["verdict"] == "eligible" and crit_map.get(v["criterion_id"]) and crit_map[v["criterion_id"]].mandatory) total_m = sum(1 for v in verdicts if crit_map.get(v["criterion_id"]) and crit_map[v["criterion_id"]].mandatory) st.markdown("
", unsafe_allow_html=True) with st.container(border=True): # Header st.markdown( f'
' f'
' f'
🏢
' f'
' f'
{name}
' f'
' f'{tagline}
' f'
' f'{verdict_pill(ov)}' f'' f'{passed}/{total_m} mandatory passed' f'
', unsafe_allow_html=True, ) with st.expander(f"View all {len(verdicts)} criteria", expanded=False): # Column headers st.markdown( '
' + "".join( f'
{h}
' for h in ["Criterion", "Verdict", "Extracted Value", "Source & Tier", "Category"] ) + "
", unsafe_allow_html=True, ) for v in verdicts: crit = crit_map.get(v["criterion_id"]) title = crit.title if crit else v["criterion_id"] mb = mandatory_badge(crit.mandatory if crit else True) cat = category_badge(crit.category if crit else "compliance") extracted = v.get("extracted_value") or "" src = v.get("source") or {} src_html = '' if src: tier = ocr_tier_badge(src.get("source_type", "text_pdf")) src_html = ( f'' f'{src.get("doc_name","")}' f' ' f'p{src.get("page","")}' f'
{tier}
' ) extracted_cell = ( f'{extracted}' if extracted else '' ) st.markdown( f'
' f'
{mb}
' f'{v["criterion_id"]}: {title}
' f'
{verdict_pill(v["verdict"])}
' f'
{extracted_cell}
' f'
{src_html}
' f'
{cat}
' f'
', unsafe_allow_html=True, ) confidence_bar(v.get("combined_confidence", 0.0)) reason = v.get("reason", "") snippet = (v.get("source") or {}).get("snippet", "") if reason: st.markdown( f'
' f'Reason: {reason}
', unsafe_allow_html=True, ) if snippet: st.markdown( f'
' f'“{snippet}”
', unsafe_allow_html=True, ) st.markdown( '
', unsafe_allow_html=True, )