diff --git a/backend/src/app.ts b/backend/src/app.ts index 2debf166..5282468d 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -2,6 +2,8 @@ import express, { Express, Response } from 'express'; import initORM from './orm.js'; import themeRoutes from './routes/themes.js'; import learningPathRoutes from './routes/learningPaths.js' +import learningObjectRoutes from './routes/learningObjects.js' + const app: Express = express(); const port: string | number = process.env.PORT || 3000; @@ -15,6 +17,7 @@ app.get('/', (_, res: Response) => { app.use('/theme', themeRoutes); app.use('/learningPath', learningPathRoutes); +app.use('/learningObject', learningObjectRoutes); async function startServer() { await initORM(); diff --git a/backend/src/config/config.ts b/backend/src/config/config.ts new file mode 100644 index 00000000..414d10a6 --- /dev/null +++ b/backend/src/config/config.ts @@ -0,0 +1,9 @@ +// Can be placed in dotenv but found it redundant + +// import dotenv from "dotenv"; + +// Load .env file +// dotenv.config(); + +export const DWENGO_API_BASE = "https://dwengo.org/backend/api"; + diff --git a/backend/src/controllers/learningObjects.ts b/backend/src/controllers/learningObjects.ts new file mode 100644 index 00000000..fc020b7e --- /dev/null +++ b/backend/src/controllers/learningObjects.ts @@ -0,0 +1,20 @@ +import { Request, Response } from "express"; +import { getLearningObjectsFromPath } from "../services/learningObjects.js"; + +export async function getAllLearningObjects(req: Request, res: Response): Promise { + try { + const { hruid } = req.params; + const language = req.query.language as string || "nl"; // Default to Dutch; + + if (!language) { + res.status(400).json({ error: "Language query parameter is required." }); + return; + } + + const learningObjects = await getLearningObjectsFromPath(hruid, language); + res.json(learningObjects); + } catch (error) { + console.error("Error fetching learning objects:", error); + res.status(500).json({ error: "Internal server error" }); + } +} diff --git a/backend/src/controllers/learningPaths.ts b/backend/src/controllers/learningPaths.ts index 308fe9b2..c8cadd16 100644 --- a/backend/src/controllers/learningPaths.ts +++ b/backend/src/controllers/learningPaths.ts @@ -1,16 +1,7 @@ import { Request, Response } from "express"; import axios from "axios"; import { themes } from "../data/themes.js"; -import dotenv from "dotenv"; - -// Load environment variables -dotenv.config(); - -// Get API base URL from environment variables -const DWENGO_API_BASE = process.env.DWENGO_API_BASE as string; -if (!DWENGO_API_BASE) { - throw new Error("DWENGO_API_BASE is not defined in the .env file"); -} +import { DWENGO_API_BASE } from "../config/config.js"; /** * Fetch learning paths for a given list of HRUIDs. diff --git a/backend/src/routes/learningObjects.ts b/backend/src/routes/learningObjects.ts new file mode 100644 index 00000000..886b42f3 --- /dev/null +++ b/backend/src/routes/learningObjects.ts @@ -0,0 +1,11 @@ +import express from "express"; +import { getAllLearningObjects } from "../controllers/learningObjects.js"; + +const router = express.Router(); + +// arg: hruid learningPath +// query: language +// Route to fetch list of learning objects based on hruid of learning path +router.get("/:hruid", getAllLearningObjects); + +export default router; diff --git a/backend/src/routes/learningPaths.ts b/backend/src/routes/learningPaths.ts index 84e5367e..5b49937d 100644 --- a/backend/src/routes/learningPaths.ts +++ b/backend/src/routes/learningPaths.ts @@ -3,15 +3,20 @@ import { getLearningPathsFromIds, getLearningPathsByTheme, getAllLearningPaths, const router = express.Router(); +// query: language // Route to fetch learning paths based on a list of HRUIDs router.get("/", getLearningPathsFromIds); +// query: language // Route to fetch all possible learning paths router.get("/all", getAllLearningPaths); +// query: language // Route to fetch learning paths based on a searchterm router.get("/search", searchLearningPaths); +// arg: theme id +// query: language // Route to fetch learning paths based on a theme router.get("/theme/:theme", getLearningPathsByTheme); diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learningObjects.ts new file mode 100644 index 00000000..6e58defb --- /dev/null +++ b/backend/src/services/learningObjects.ts @@ -0,0 +1,67 @@ +import axios from "axios"; +import { DWENGO_API_BASE } from "../config/config.js"; + +interface LearningObjectNode { + _id: string; + learningobject_hruid: string; + version: number; + language: string; +} + + +export async function getLearningObjectsFromPath(hruid: string, language: string) { + try { + const learningPathUrl = `${DWENGO_API_BASE}/learningPath/${hruid}/${language}`; + const learningPathResponse = await axios.get(learningPathUrl); + const nodes = learningPathResponse.data.nodes; + + if (!nodes || nodes.length === 0) { + throw new Error("No learning objects found in this learning path."); + } + + return await Promise.all( + nodes.map(async (node: LearningObjectNode) => { + const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata?hruid=${node.learningobject_hruid}&version=${node.version}&language=${language}`; + const metadataResponse = await axios.get(metadataUrl); + + const html_url = `${DWENGO_API_BASE}/learningObject/getRaw?hruid=${node.learningobject_hruid}&version=${node.version}&language=${language}`; + + return filterLearningObjectMetadata(metadataResponse.data, html_url); + }) + ); + + } catch (error) { + console.error("Error fetching learning objects:", error); + throw new Error("Failed to fetch learning objects."); + } +} + +function filterLearningObjectMetadata(data: any, html_url: String) { + return { + key: data.hruid, + // hruid learningObject (not path) + _id: data._id, + uuid: data.uuid, + version: data.version, + + title: data.title, + html_url, + // html content object + language: data.language, + difficulty: data.difficulty, + estimated_time: data.estimated_time, + available: data.available, + teacher_exclusive: data.teacher_exclusive, + educational_goals: data.educational_goals, + // list with learningObjects + keywords: data.keywords, + // for search + description: data.description, + // for search (not an actual description) + target_ages: data.target_ages, + + // skos concepts needed ?? + // content type needed ?? + // content location ?? + }; +}