File size: 6,656 Bytes
661eb14
 
b2ad034
01f6914
b2ad034
 
 
 
 
1049372
b2ad034
1049372
b2ad034
 
661eb14
1049372
aff4140
 
 
1049372
 
 
b2ad034
1049372
 
b2ad034
 
 
1049372
aff4140
 
 
 
 
 
 
b2ad034
1049372
 
b2ad034
 
1049372
aff4140
 
 
1049372
 
aff4140
1049372
aff4140
 
 
1049372
 
b14fc84
b2ad034
1049372
 
b2ad034
1049372
b2ad034
 
1049372
 
 
 
aff4140
 
 
 
1049372
 
 
 
 
 
 
 
b2ad034
01f6914
aff4140
 
01f6914
aff4140
 
01f6914
 
b2ad034
01f6914
aff4140
 
 
 
01f6914
 
 
b2ad034
01f6914
aff4140
 
 
 
01f6914
 
1049372
 
b2ad034
1049372
 
 
b2ad034
aff4140
 
 
 
 
1049372
aff4140
 
1049372
 
aff4140
b2ad034
1049372
 
 
 
 
 
 
 
 
aff4140
b2ad034
1049372
 
 
 
aff4140
b2ad034
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
131
132
133
134
135
136
137
138
139
140
141
import streamlit as st

from core import audit
from core.config import BIDDER_NAMES
from core.fallback import load_criteria
from core.schemas import Criterion
from ui.components import confidence_bar, verdict_pill


def _crit_map() -> dict[str, Criterion]:
    data = st.session_state.get("criteria")
    return {c["id"]: Criterion(**c) for c in data} if data else {c.id: c for c in load_criteria()}


def render() -> None:
    st.markdown(
        '<h2 style="font-weight:800;font-size:1.5rem;color:var(--text-color);">'
        'Human Review Queue</h2>'
        '<p style="color:var(--text-color);opacity:0.6;font-size:0.875rem;margin-bottom:1rem;">'
        'Verdicts that could not be automatically confirmed require officer sign-off.</p>',
        unsafe_allow_html=True,
    )

    vdata: dict = st.session_state.get("verdicts", {})
    if not vdata:
        st.info("No evaluation results yet. Run the evaluation in the Bidder Evaluation tab first.")
        return

    cm = _crit_map()
    pending = [
        (bid, i, v)
        for bid, verdicts in vdata.items()
        for i, v in enumerate(verdicts)
        if v.get("verdict") == "needs_review" and
        v.get("review_status", "pending") == "pending"
    ]

    if not pending:
        st.success("✅ All flagged verdicts have been actioned. Nothing pending.")
        return

    st.markdown(
        f'<div style="background:rgba(245,158,11,0.1);border:1px solid rgba(245,158,11,0.3);'
        f'border-radius:10px;padding:14px 18px;margin-bottom:1.5rem;'
        f'display:flex;align-items:center;gap:10px;">'
        f'<span style="font-size:1.3rem;">⚠️</span>'
        f'<div>'
        f'<div style="font-weight:700;color:#F59E0B;font-size:0.9rem;">'
        f'{len(pending)} item{"s" if len(pending) != 1 else ""} pending review</div>'
        f'<div style="font-size:0.8rem;color:var(--text-color);opacity:0.6;margin-top:2px;">'
        f'High certainty = model is confident this needs human judgment, '
        f'not that the bidder is ineligible.</div>'
        f'</div></div>',
        unsafe_allow_html=True,
    )

    for bid, idx, v in pending:
        crit = cm.get(v["criterion_id"])
        crit_title = crit.title if crit else v["criterion_id"]
        company = BIDDER_NAMES.get(bid, bid)

        with st.container(border=True):
            st.markdown(
                f'<div style="display:flex;justify-content:space-between;'
                f'align-items:flex-start;gap:12px;margin-bottom:10px;">'
                f'<div>'
                f'<div style="font-weight:700;font-size:0.95rem;color:var(--text-color);">'
                f'{company}</div>'
                f'<div style="font-size:0.82rem;color:var(--text-color);opacity:0.55;'
                f'margin-top:2px;">{v["criterion_id"]}: {crit_title}</div>'
                f'</div>'
                f'{verdict_pill(v["verdict"])}'
                f'</div>',
                unsafe_allow_html=True,
            )

            col_l, col_r = st.columns([3, 1])
            with col_l:
                if v.get("extracted_value"):
                    st.markdown(
                        f'<div style="font-size:0.84rem;margin-bottom:8px;'
                        f'color:var(--text-color);">'
                        f'<strong>Extracted value:</strong> '
                        f'<code style="background:rgba(128,128,128,0.12);padding:2px 7px;'
                        f'border-radius:4px;">{v["extracted_value"]}</code></div>',
                        unsafe_allow_html=True,
                    )
                if v.get("reason"):
                    st.markdown(
                        f'<div style="background:rgba(245,158,11,0.08);'
                        f'border-left:3px solid #F59E0B;padding:9px 13px;'
                        f'border-radius:0 7px 7px 0;font-size:0.84rem;'
                        f'color:var(--text-color);margin-bottom:8px;">'
                        f'<strong>Reason:</strong> {v["reason"]}</div>',
                        unsafe_allow_html=True,
                    )
                if v.get("source") and v["source"].get("snippet"):
                    st.markdown(
                        f'<div style="background:rgba(128,128,128,0.07);'
                        f'border:1px solid rgba(128,128,128,0.15);padding:9px 13px;'
                        f'border-radius:7px;font-size:0.82rem;color:var(--text-color);'
                        f'font-style:italic;">&ldquo;{v["source"]["snippet"]}&rdquo;</div>',
                        unsafe_allow_html=True,
                    )
            with col_r:
                confidence_bar(v.get("combined_confidence", 0.0), "Certainty")

            st.markdown("<div style='height:8px'></div>", unsafe_allow_html=True)
            kp = f"rv_{bid}_{v['criterion_id']}"
            bc1, bc2, bc3 = st.columns(3)

            common = dict(bidder_id=bid, criterion_id=v["criterion_id"],
                          original_verdict=v["verdict"],
                          original_extracted_value=v.get("extracted_value", ""),
                          combined_confidence=v.get("combined_confidence", 0.0))

            with bc1:
                if st.button("✅ Approve", key=f"{kp}_approve",
                             use_container_width=True, type="primary"):
                    st.session_state["verdicts"][bid][idx]["review_status"] = "approved"
                    audit.log("human_review_action", actor="officer",
                              action_taken="approved", **common)
                    st.rerun()
            with bc2:
                edited = st.text_input("Corrected value", key=f"{kp}_edit_val",
                                       placeholder="Optional override…",
                                       label_visibility="collapsed")
                if st.button("✏️ Edit & Approve", key=f"{kp}_edit", use_container_width=True):
                    st.session_state["verdicts"][bid][idx]["review_status"] = "edited"
                    if edited:
                        st.session_state["verdicts"][bid][idx]["extracted_value"] = edited
                    audit.log("human_review_action", actor="officer",
                              action_taken="edited", edited_value=edited, **common)
                    st.rerun()
            with bc3:
                if st.button("❌ Reject", key=f"{kp}_reject", use_container_width=True):
                    st.session_state["verdicts"][bid][idx]["review_status"] = "rejected"
                    audit.log("human_review_action", actor="officer",
                              action_taken="rejected", **common)
                    st.rerun()