"""Render a single listing row with trust score, badges, and error states.""" from __future__ import annotations import json import streamlit as st from app.db.models import Listing, TrustScore, Seller from typing import Optional def _score_colour(score: int) -> str: if score >= 80: return "๐ŸŸข" if score >= 50: return "๐ŸŸก" return "๐Ÿ”ด" def _flag_label(flag: str) -> str: labels = { "new_account": "โœ— New account", "account_under_30_days": "โš  Account <30d", "low_feedback_count": "โš  Low feedback", "suspicious_price": "โœ— Suspicious price", "duplicate_photo": "โœ— Duplicate photo", "established_bad_actor": "โœ— Bad actor", "marketing_photo": "โœ— Marketing photo", } return labels.get(flag, f"โš  {flag}") def render_listing_row( listing: Listing, trust: Optional[TrustScore], seller: Optional[Seller] = None, ) -> None: col_img, col_info, col_score = st.columns([1, 5, 2]) with col_img: if listing.photo_urls: # Spec requires graceful 404 handling: show placeholder on failure try: import requests as _req r = _req.head(listing.photo_urls[0], timeout=3, allow_redirects=True) if r.status_code == 200: st.image(listing.photo_urls[0], width=80) else: st.markdown("๐Ÿ“ท *Photo unavailable*") except Exception: st.markdown("๐Ÿ“ท *Photo unavailable*") else: st.markdown("๐Ÿ“ท *No photo*") with col_info: st.markdown(f"**{listing.title}**") if seller: age_str = f"{seller.account_age_days // 365}yr" if seller.account_age_days >= 365 \ else f"{seller.account_age_days}d" st.caption( f"{seller.username} ยท {seller.feedback_count} fb ยท " f"{seller.feedback_ratio*100:.1f}% ยท member {age_str}" ) else: st.caption(f"{listing.seller_platform_id} ยท *Seller data unavailable*") if trust: flags = json.loads(trust.red_flags_json or "[]") if flags: badge_html = " ".join( f'{_flag_label(f)}' for f in flags ) st.markdown(badge_html, unsafe_allow_html=True) if trust.score_is_partial: st.caption("โš  Partial score โ€” some data unavailable") else: st.caption("โš  Could not score this listing") with col_score: if trust: icon = _score_colour(trust.composite_score) st.metric(label="Trust", value=f"{icon} {trust.composite_score}") else: st.metric(label="Trust", value="?") st.markdown(f"**${listing.price:,.0f}**") st.markdown(f"[Open eBay โ†—]({listing.url})") st.divider()