| import streamlit as st |
| import pandas as pd |
| from rdflib import Graph, Namespace |
| import os |
|
|
| |
| st.set_page_config(page_title="Cars Knowledge Graph Demo", layout="wide") |
|
|
| |
| @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() |
|
|
| |
| EX = Namespace("http://example.org/cars/") |
|
|
| |
| st.sidebar.header("Filter Cars") |
|
|
| |
| manu_query = """ |
| PREFIX ex: <http://example.org/cars/> |
| PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
| 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) |
|
|
| |
| price_query = """ |
| PREFIX ex: <http://example.org/cars/> |
| 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) |
|
|
| |
| hp_query = """ |
| PREFIX ex: <http://example.org/cars/> |
| 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) |
|
|
| |
| st.title("🚗 Cars Knowledge Graph Explorer") |
| st.markdown("This application queries the RDF Knowledge Graph directly using **SPARQL**.") |
|
|
| |
| sparql_query = f""" |
| PREFIX ex: <http://example.org/cars/> |
| PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
| |
| 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 |
| """ |
|
|
| |
| results = g.query(sparql_query) |
|
|
| |
| 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.") |
|
|
| |
| with st.expander("Run Custom SPARQL Query"): |
| custom_query = st.text_area("SPARQL Query", """ |
| PREFIX ex: <http://example.org/cars/> |
| 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}") |
|
|