#!/usr/bin/env python3 import asyncio import json import urllib.parse import streamlit as st from sqlalchemy import text from app.database import AsyncSessionLocal # Streamlit oldal alapbeállításai st.set_page_config( page_title="Service Finder - HITL Adattisztító", page_icon="🔧", layout="wide" ) # --- ADATBÁZIS MŰVELETEK (Hardened Stateless Logic) --- async def get_review_vehicle(): """Lekérdez egy javításra váró járművet izolált sessionben.""" async with AsyncSessionLocal() as session: try: query = text(""" SELECT id, make, marketing_name, year_from, fuel_type, raw_api_data, raw_search_context, trim_level, body_type, power_kw, engine_capacity, specifications, last_error FROM vehicle.vehicle_model_definitions WHERE status = 'manual_review_needed' ORDER BY priority_score DESC LIMIT 1 """) result = await session.execute(query) row = result.fetchone() if not row: return None vehicle = dict(row._mapping) # URL bányászat a JSON adatokból source_url = None if vehicle.get('raw_api_data'): api_data = vehicle['raw_api_data'] if isinstance(api_data, str): try: api_data = json.loads(api_data) except: api_data = {} source_url = api_data.get('url') or api_data.get('source_url') or api_data.get('link') vehicle['extracted_url'] = source_url return vehicle except Exception as e: st.error(f"❌ Lekérdezési hiba: {e}") return None finally: # Garantáljuk a session lezárását await session.close() async def update_vehicle_data(vehicle_id, updates, new_status): """Elmenti az adatokat és azonnal felszabadítja a hálózati erőforrásokat.""" session = AsyncSessionLocal() try: # Dinamikus SQL összeállítása set_items = [f"{k} = :{k}" for k in updates.keys()] set_clause = ", ".join(set_items) sql = text(f""" UPDATE vehicle.vehicle_model_definitions SET status = :status, {set_clause}, updated_at = NOW() WHERE id = :id """) params = {"status": new_status, "id": vehicle_id, **updates} await session.execute(sql, params) await session.commit() return True except Exception as e: await session.rollback() st.error(f"❌ Mentési hiba az adatbázisban: {e}") return False finally: # KRITIKUS JAVÍTÁS: Explicit lezárás, hogy ne maradjon nyitott transport await session.close() # Itt kényszerítjük a kapcsolat-kezelőt a háttérben futó motor elengedésére bind = session.bind if bind: await bind.dispose() # --- UI LOGIKA --- async def main_async(): st.title("🔧 HITL Adattisztító - Autó Adat Javítás") # Adat betöltése a memóriába (ha üres) if "current_vehicle" not in st.session_state or st.session_state.current_vehicle is None: with st.spinner("Adatbázis szinkronizálása..."): st.session_state.current_vehicle = await get_review_vehicle() v = st.session_state.current_vehicle if not v: st.success("🎉 Minden jármű ellenőrizve!") if st.button("🔄 Új lekérdezés"): st.session_state.current_vehicle = None st.rerun() return # Felület felépítése st.header(f"🚗 {v['year_from'] or '????'} {v['make']} {v['marketing_name']}") st.caption(f"DB ID: {v['id']} | Üzemanyag: {v['fuel_type'] or 'n/a'}") # 3 oszlopos nézet col_raw, col_source, col_edit = st.columns([1, 1, 1.2]) with col_raw: st.subheader("📄 Robot Naplók") if v['raw_api_data']: with st.expander("Nyers JSON (API)", expanded=True): st.json(v['raw_api_data']) with st.expander("Keresési Környezet", expanded=False): st.text_area("Talált szövegek", v['raw_search_context'] or "Nincs adat", height=400) with col_source: st.subheader("🔗 Eredeti Források") if v['extracted_url']: st.success("📍 Közvetlen adatlap linkje:") st.markdown(f"### [FORRÁS MEGNYITÁSA ↗️]({v['extracted_url']})") else: st.warning("⚠️ Nincs közvetlen link.") st.markdown("---") st.write("**Segédeszközök:**") search_q = urllib.parse.quote(f"{v['make']} {v['marketing_name']} {v['year_from'] or ''} specifications") st.markdown(f"- [🔍 Google Keresés](https://www.google.com/search?q={search_q})") us_query = urllib.parse.quote(f"{v['make']} {v['marketing_name']}") us_url = f"https://www.google.com/search?q=site:ultimatespecs.com+{us_query}" if v['specifications']: with st.expander("Már meglévő specifikációk", expanded=True): st.json(v['specifications']) with col_edit: st.subheader("✏️ Adatbevitel") with st.form("hitl_form_v2", clear_on_submit=False): trim = st.text_input("Trim / Felszereltség", value=v['trim_level'] or "") body_opts = ["", "SEDAN", "HATCHBACK", "SUV", "ESTATE", "COUPE", "CONVERTIBLE", "VAN", "PICKUP", "MPV"] curr_body = v['body_type'] if v['body_type'] in body_opts else "" body = st.selectbox("Karosszéria", body_opts, index=body_opts.index(curr_body)) pwr = st.number_input("Teljesítmény (kW)", value=int(v['power_kw'] or 0)) cap = st.number_input("Hengerűrtartalom (cm³)", value=int(v['engine_capacity'] or 0)) st.markdown("---") comment = st.text_area("Megjegyzés (második zsák adatai)", placeholder="További kiegészítő adatok...") st.write("") b1, b2, b3 = st.columns(3) save_btn = b1.form_submit_button("💾 MENTÉS", type="primary") skip_btn = b2.form_submit_button("⏭️ KIHAGYÁS") reject_btn = b3.form_submit_button("🗑️ KUKA") # Mentési logika if save_btn: updates = { "trim_level": trim, "body_type": body, "power_kw": pwr, "engine_capacity": cap, "last_error": f"Manual fix OK. {comment}".strip() } with st.spinner("Véglegesítés..."): if await update_vehicle_data(v['id'], updates, "published"): st.session_state.current_vehicle = None st.rerun() if skip_btn: st.session_state.current_vehicle = None st.rerun() if reject_btn: if await update_vehicle_data(v['id'], {"last_error": "Manual rejection"}, "rejected"): st.session_state.current_vehicle = None st.rerun() if __name__ == "__main__": asyncio.run(main_async())