A comprehensive example application demonstrating how to interact with the DINI-AG-KIM curriculum ontology and triple store data using pure HTML, CSS, and JavaScript.
This standalone repository provides a complete, working example of how to:
- Query curriculum ontologies using SPARQL
- Build interactive hierarchical tree visualizations
- Handle German educational data with proper label display
- Create responsive web applications without build tools
Perfect for: Developers, researchers, students, and data scientists working with educational ontologies and semantic web technologies.
- State Selection: Browse German federal states (BundeslΓ€nder)
- Subject Filtering: Filter curricula by academic subjects
- Class Level Selection: Choose specific grade levels (Jahrgangsstufen)
- Hierarchical Tree: Expandable curriculum structure visualization
- German Labels: Human-readable labels with intelligent fallbacks
- Real-time SPARQL Display: See queries as they execute
- API Response Viewer: Inspect raw JSON responses
- Debug Logging: Comprehensive console output
- Endpoint Switching: Toggle between development and production
- Performance Caching: Client-side caching for better performance
- Progressive Examples: Step-by-step complexity increase
- Comprehensive Documentation: Extensive inline and external docs
- SPARQL Pattern Library: Reusable query patterns
- Best Practices: Production-ready code patterns
curriculum-browser-example/
βββ index.html # Main application interface
βββ main.js # Application logic and event handling
βββ sparql-examples.js # SPARQL query functions and patterns
βββ tree-builder.js # Tree visualization and lazy loading
βββ styles.css # Responsive CSS styling
βββ README.md # This documentation
βββ LICENSE # MIT license
βββ .gitignore # Git ignore patterns
βββ memory-bank/ # Development context and documentation
βββ projectbrief.md # Project overview and goals
βββ techContext.md # Technical architecture details
βββ progress.md # Development timeline and status
βββ systemPatterns.md # Code patterns and best practices
βββ activeContext.md # Current development context
- Modern Web Browser: Chrome, Firefox, Safari, or Edge
- SPARQL Endpoint: Access to GraphDB or similar triple store
- Basic Knowledge: HTML, JavaScript, and SPARQL fundamentals
- Download or Clone this repository
- Configure Endpoint in
sparql-examples.js:const SPARQL_ENDPOINTS = { development: 'http://localhost:7200/repositories/your-repo', production: 'https://your-endpoint.com/repositories/curriculum' };
- Open
index.htmlin your browser or serve via HTTP server - Start Exploring curriculum data!
-
Get Curriculum Data:
git clone https://github.com/dini-ag-kim/school-curriculum-pg.git
-
Set Up GraphDB:
- Install GraphDB (free edition available)
- Create a new repository
- Import ontology files from
school-curriculum-pg/src/ontology/ - Import curriculum data files (
.owlor.ttlfiles)
-
Configure and Run:
- Update endpoint URLs in
sparql-examples.js - Serve files via HTTP server (required for CORS):
# Python 3 python -m http.server 8000 # Python 2 python -m SimpleHTTPServer 8000 # Node.js (if available) npx http-server # PHP (if available) php -S localhost:8000
- Open
http://localhost:8000in your browser
- Update endpoint URLs in
// Query all German federal states
const states = await SPARQLExamples.queryStates();
// Query subjects for a specific state
const subjects = await SPARQLExamples.querySubjectsByState('https://w3id.org/lehrplan/BW');
// Query curricula with multiple filters
const curricula = await SPARQLExamples.queryCurriculaByStateSubjectAndClassLevel(
'https://w3id.org/lehrplan/BW',
'https://w3id.org/lehrplan/subject/Mathematics',
'https://w3id.org/lehrplan/level/5'
);// Build interactive curriculum tree
await TreeBuilder.buildCurriculumTree(curriculumUri, containerElement, {
maxDepth: 4,
showLabels: true
});
// Expand/collapse all nodes
TreeBuilder.expandAll(containerElement);
TreeBuilder.collapseAll(containerElement);// Execute custom SPARQL query
const customQuery = `
PREFIX lp: <https://w3id.org/lehrplan/ontology/>
SELECT * WHERE {
?s a lp:LP_0000438 .
?s lp:LP_0000029 <https://w3id.org/lehrplan/BW> .
} LIMIT 10
`;
const results = await SPARQLExamples.executeSPARQLQuery(customQuery);Configure endpoints in sparql-examples.js:
const SPARQL_ENDPOINTS = {
development: 'http://localhost:7200/repositories/mem',
production: 'https://graphdb.edufeed.org/repositories/bayern'
};- Endpoint URLs: Change SPARQL endpoint configuration
- UI Styling: Modify
styles.cssfor custom appearance - Query Parameters: Adjust filters and limits in query functions
- Tree Visualization: Configure depth, labels, and interaction options
- LP_0000438: Curriculum (Lehrplan)
- LP_0000261: Curricular Element
- LP_0001015: CE-Fragment
- LP_0000029: hasState (Bundesland)
- LP_0000537: hasSubject (Fach)
- LP_0000026: hasClassLevel (Jahrgangsstufe)
- lp:hasPart: Hierarchical relationships
- rdfs:label: Human-readable labels (German)
The main example (index.html) demonstrates:
-
π State Selection
- Loads all German federal states
- Shows SPARQL query used
- Displays API response format
-
π Subject Selection
- Filters subjects by selected state
- Cascading dropdown behavior
- German language labels
-
π Curriculum Selection
- Filters curricula by selected state
- Shows curriculum URIs and labels
-
π³ Tree Visualization
- Builds hierarchical tree from curriculum data
- Expandable/collapsible nodes
- Shows parent-child relationships
As you interact with the example, it shows:
- π Raw SPARQL queries with syntax highlighting
- π‘ API responses in JSON format
- π» Code examples for each operation
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX lp: <https://w3id.org/lehrplan/ontology/>
SELECT DISTINCT ?uri ?label
WHERE {
?s lp:LP_0000029 ?uri .
?uri rdfs:label ?label .
FILTER(lang(?label) = "de")
}
ORDER BY ?labelJavaScript Usage:
const states = await SPARQLExamples.queryStates();PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX lp: <https://w3id.org/lehrplan/ontology/>
SELECT DISTINCT ?uri (SAMPLE(?label1) AS ?label)
WHERE {
?s lp:LP_0000537 ?uri .
?uri rdfs:label ?label1 .
?s lp:LP_0000029 <https://w3id.org/lehrplan/BW> .
FILTER(lang(?label1) = "de")
}
GROUP BY ?uri
ORDER BY ?labelJavaScript Usage:
const subjects = await SPARQLExamples.querySubjectsByState('https://w3id.org/lehrplan/BW');PREFIX lp: <https://w3id.org/lehrplan/ontology/>
SELECT DISTINCT ?child
WHERE {
<https://w3id.org/lehrplan/BW/Mathe/5> lp:hasPart ?child .
}JavaScript Usage:
const children = await SPARQLExamples.queryChildren('https://w3id.org/lehrplan/BW/Mathe/5');The tree building uses a lazy loading approach that loads curriculum hierarchy on-demand to improve performance and reduce initial load times.
// Create root node with lazy loading
const rootNode = await createTreeNodeWithLazyLoading(curriculumUri, 0, maxDepth, showLabels);
// When user expands a node, children are loaded dynamically
async function toggleNodeWithLazyLoading(nodeElement, nodeUri, depth, maxDepth, showLabels) {
// Query children only when needed
const children = await window.SPARQLExamples.queryChildren(nodeUri);
// Create child nodes recursively
for (const childUri of children) {
const childElement = await createTreeNodeWithLazyLoading(childUri, depth + 1, maxDepth, showLabels);
childrenContainer.appendChild(childElement);
}
}// Simple cache to avoid repeated label queries
const labelCache = new Map();
// Check cache first, then fetch if needed
if (labelCache.has(nodeUri)) {
label = labelCache.get(nodeUri);
} else {
label = await window.SPARQLExamples.queryLabel(nodeUri);
labelCache.set(nodeUri, label); // Cache for future use
}The implementation uses multiple query strategies with automatic fallbacks:
// Primary query using lp:hasPart
const query = `SELECT DISTINCT ?child WHERE { <${elementUri}> lp:hasPart ?child . }`;
// If primary fails, try alternative relationships
const altQuery = `SELECT DISTINCT ?child WHERE {
?child lp:LP_0020001 <${elementUri}> .
}`;βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β index.html β β sparql-examples β β SPARQL β
β (UI) βββββΊβ .js βββββΊβ Endpoint β
β β β (Queries) β β β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β main.js β β tree-builder β
β (Logic) βββββΊβ .js β
β β β (Visualizationβ
βββββββββββββββββββ βββββββββββββββββββ
index.html: User interface and layoutmain.js: Application logic and event handlingsparql-examples.js: SPARQL query functionstree-builder.js: Tree visualization and DOM manipulationstyles.css: Visual styling and responsive design
Edit sparql-examples.js:
const SPARQL_ENDPOINTS = {
development: 'http://your-local-endpoint:7200/repositories/repo',
production: 'https://your-production-endpoint.com/sparql'
};Edit styles.css:
.tree-node {
/* Customize tree node appearance */
}
.tree-children {
/* Customize child container styling */
}Add to sparql-examples.js:
async function queryCustomData() {
const query = `
PREFIX lp: <https://w3id.org/lehrplan/ontology/>
SELECT * WHERE {
?s your:customProperty ?o .
}
`;
return await executeSPARQLQuery(query);
}-
CORS Errors
- Ensure your SPARQL endpoint allows cross-origin requests
- Or serve the examples from the same domain as the endpoint
- Use a local HTTP server instead of opening files directly
-
No Data Returned
- Verify the ontology data is loaded in your SPARQL endpoint
- Check that the correct repository/graph is being queried
- Ensure endpoint URL is correct and accessible
-
Tree Not Building
- Ensure the curriculum has hierarchical relationships (
hasPart) - Check browser console for JavaScript errors
- Verify SPARQL endpoint is responding correctly
- Ensure the curriculum has hierarchical relationships (
-
Labels Not Displaying
- Check if labels exist in the ontology data
- Verify language tags are correct (German: "de")
- Enable debug logging to see label fetching attempts
Enable debug logging:
// In browser console
console.log(window.SPARQLExamples);
console.log(window.TreeBuilder);
console.log(window.CurriculumBrowser);- Slow Queries: Reduce query complexity or add LIMIT clauses
- Large Trees: Consider implementing virtual scrolling
- Memory Usage: Clear caches periodically for long-running sessions
The application supports dynamic endpoint switching:
// Switch to development endpoint
SPARQLExamples.setEndpoint(SPARQLExamples.SPARQL_ENDPOINTS.development);
// Switch to production endpoint
SPARQLExamples.setEndpoint(SPARQLExamples.SPARQL_ENDPOINTS.production);
// Check current endpoint
console.log('Current endpoint:', SPARQLExamples.CURRENT_ENDPOINT);- Development:
http://localhost:7200/repositories/mem(local GraphDB) - Production:
https://graphdb.edufeed.org/repositories/bayern(live data)
This repository includes a comprehensive memory-bank system in the memory-bank/ directory:
projectbrief.md: Project overview, goals, and featurestechContext.md: Technical architecture and implementation detailsprogress.md: Development timeline and completed milestonessystemPatterns.md: Code patterns, best practices, and examplesactiveContext.md: Current development status and next steps
All functions are documented with JSDoc comments. Key modules include:
SPARQLExamples: Query functions, endpoint management, and data processingTreeBuilder: Tree visualization, lazy loading, and DOM manipulationCurriculumBrowser: Main application logic and UI coordination
SPARQLExamples.queryChildren(uri): Get hierarchical children usinglp:hasPartSPARQLExamples.queryLabel(uri): Fetch human-readable labels with cachingSPARQLExamples.setEndpoint(url): Switch between development/production endpointsTreeBuilder.buildCurriculumTree(uri, container, options): Build lazy-loading treeTreeBuilder.expandAll(container): Expand all tree nodesTreeBuilder.collapseAll(container): Collapse all tree nodes
LP_0000438: Curriculum (Lehrplan)LP_0000261: Curricular ElementLP_0001015: CE-Fragment
LP_0000029: hasState (Bundesland)LP_0000537: hasSubject (Fach)LP_0000026: hasClassLevel (Jahrgangsstufe)lp:hasPart: Hierarchical parent-child relationshipslp:LP_0020001: Alternative hierarchical relationships (used in fallbacks)rdfs:label: Human-readable labels (German)
This example code is provided as-is for educational purposes. The curriculum ontology data may be subject to separate licensing terms from DINI-AG-KIM.