import streamlit as st import pandas as pd from rdflib import Graph, Namespace import os # Page Config st.set_page_config(page_title="Cars Knowledge Graph Demo", layout="wide") # Load Graph @st.cache_resource def load_graph(): g = Graph() graph_path = "cars_knowledge_graph.ttl" if os.path.exists(graph_path): g.parse(graph_path, format="turtle") return g try: g = load_graph() except Exception as e: st.error(f"Failed to load graph: {e}") st.stop() if len(g) == 0: st.warning("Graph is empty or not found. Please run 'src/convert_data.py' first.") st.stop() # Namespaces EX = Namespace("http://example.org/cars/") # Sidebar Filters st.sidebar.header("Filter Cars") # 1. Manufacturer Filter manu_query = """ PREFIX ex: PREFIX rdfs: SELECT DISTINCT ?name WHERE { ?m a ex:Manufacturer ; rdfs:label ?name . } ORDER BY ?name """ manufacturers = ["All"] + [str(row.name) for row in g.query(manu_query)] selected_manu = st.sidebar.selectbox("Manufacturer", manufacturers) # 2. Price Range price_query = """ PREFIX ex: SELECT (MIN(?p) as ?min) (MAX(?p) as ?max) WHERE { ?s ex:hasPriceValue ?p } """ price_res = list(g.query(price_query))[0] min_price, max_price = float(price_res.min), float(price_res.max) selected_price = st.sidebar.slider("Max Price (USD)", min_price, max_price, max_price) # 3. Min Horsepower hp_query = """ PREFIX ex: SELECT (MIN(?hp) as ?min) (MAX(?hp) as ?max) WHERE { ?s ex:hasHorsePowerValue ?hp } """ hp_res = list(g.query(hp_query))[0] min_hp, max_hp = int(hp_res.min), int(hp_res.max) selected_hp = st.sidebar.slider("Min Horsepower", min_hp, max_hp, min_hp) # Main Area st.title("🚗 Cars Knowledge Graph Explorer") st.markdown("This application queries the RDF Knowledge Graph directly using **SPARQL**.") # Construct Query based on filters sparql_query = f""" PREFIX ex: PREFIX rdfs: SELECT ?carName ?manuName ?price ?hp ?topSpeed ?seats WHERE {{ ?car a ex:Car ; rdfs:label ?carName ; ex:hasManufacturer ?manu ; ex:hasPriceValue ?price ; ex:hasHorsePowerValue ?hp ; ex:hasTopSpeedKMH ?topSpeed ; ex:hasSeatCount ?seats . ?manu rdfs:label ?manuName . FILTER (?price <= {selected_price}) FILTER (?hp >= {selected_hp}) {f'FILTER (?manuName = "{selected_manu}")' if selected_manu != "All" else ""} }} ORDER BY DESC(?price) LIMIT 100 """ # Run Query results = g.query(sparql_query) # Display Results data = [] for row in results: data.append({ "Car Model": str(row.carName), "Manufacturer": str(row.manuName), "Price ($)": f"${float(row.price):,.2f}", "Horsepower": int(row.hp), "Top Speed (km/h)": int(row.topSpeed), "Seats": int(row.seats) }) df = pd.DataFrame(data) col1, col2, col3 = st.columns(3) col1.metric("Total Cars Found", len(df)) col2.metric("Graph Triples", len(g)) col3.metric("Selected Manufacturer", selected_manu) if not df.empty: st.dataframe(df, use_container_width=True) else: st.info("No cars match your filters.") # Advanced: Raw SPARQL with st.expander("Run Custom SPARQL Query"): custom_query = st.text_area("SPARQL Query", """ PREFIX ex: SELECT ?name ?price WHERE { ?c ex:hasPriceValue ?price ; rdfs:label ?name . } LIMIT 5 """) if st.button("Run Query"): try: raw_res = g.query(custom_query) st.write(list(raw_res)) except Exception as e: st.error(f"Error: {e}")