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}")