Skip to content

Commit

Permalink
Merge pull request #12 from vinayakjaas/video_player
Browse files Browse the repository at this point in the history
Server side code for video content player and Documentation for XAPI
  • Loading branch information
rks-031 authored Aug 13, 2024
2 parents 46c2fc0 + e48f885 commit cce14ef
Show file tree
Hide file tree
Showing 7 changed files with 1,787 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "XAPI(Experience API)",
"position": 2,
"link": {
"type": "generated-index",
"description": "Pdf Library Introduction "
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
sidebar_position: 1
---
# Transferring Data from xAPI to SCORM Cloud

## Introduction

This guide provides a step-by-step approach for transferring data from an xAPI-based system to SCORM Cloud using JavaScript and the TinCanJS package. SCORM Cloud is a cloud-based service that supports SCORM and xAPI (Tin Can API) content. TinCanJS is a JavaScript library that facilitates communication with xAPI endpoints.

## Prerequisites

- **Node.js**: Ensure you have Node.js installed on your system.
- **NPM/Yarn**: Node package manager to install dependencies.
- **SCORM Cloud Account**: Access to SCORM Cloud credentials (endpoint, key, and secret).
- **TinCanJS Library**: JavaScript library for xAPI interactions.

## Setup

### 1. Install Dependencies

Start by setting up a new Node.js project and installing the required packages:

```bash
mkdir xapi-to-scorm
cd xapi-to-scorm
npm init -y
npm install tincanjs axios
```

### 2. Configure SCORM Cloud Credentials
Create a .env file in the root directory of your project to store SCORM Cloud credentials securely:
```bash
SCORM_CLOUD_ENDPOINT=https://cloud.scorm.com/lrs/VRIH0Z14DH/
SCORM_CLOUD_KEY=4gPlyMtlpy18FccetKM
SCORM_CLOUD_SECRET=vV4rzTZ8Q-yjnpakVIY
```
### 3. Create a JavaScript File for Data Transfer
Create a file transferData.js in the root directory of your project:
```bash
// Define the xAPI statements to be transferred
const statements = [
// Example xAPI statement
{
actor: {
mbox: "mailto:[email protected]",
name: "Example Learner"
},
verb: {
id: "http://adlnet.gov/expapi/verbs/completed",
display: { "en-US": "completed" }
},
object: {
id: "http://example.com/activities/example-activity",
objectType: "Activity",
name: { "en-US": "Example Activity" }
},
result: {
score: {
scaled: 0.9
}
},
timestamp: new Date().toISOString()
}
];
```
## Explanation
TinCanJS Configuration: Configures the library with SCORM Cloud credentials.
Statements Definition: Defines the xAPI statements that you wish to transfer.
Data Transfer Function: Sends the xAPI statements to the SCORM Cloud endpoint using Axios for HTTP requests.
Execution: Runs the data transfer script.
## Error Handling
Ensure valid SCORM Cloud credentials are provided in the .env file.
Check network connectivity and SCORM Cloud endpoint status.
Monitor the console output for successful or failed transfer messages.
## Conclusion
This documentation outlines the process of transferring data from an xAPI system to SCORM Cloud using JavaScript and the TinCanJS library. Adjust the xAPI statements as needed for your specific use case.
25 changes: 25 additions & 0 deletions Video_Player/Backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
env
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
150 changes: 150 additions & 0 deletions Video_Player/Backend/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const TinCan = require('tincanjs');
const jwt = require('jsonwebtoken');
const morgan = require('morgan');
const fs = require('fs');
const path = require('path');

const app = express();
const port = process.env.PORT || 5000;

const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' });
app.use(morgan('combined', { stream: accessLogStream }));

app.use(bodyParser.json());

app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
next();
});


const authenticateJWT = (req, res, next) => {
const token = req.header('Authorization')?.split(' ')[1];

if (token) {
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
console.error('JWT verification failed:', err);
return res.status(403).send('Forbidden');
}
req.user = user;
next();
});
} else {
res.status(401).send('Unauthorized');
}
};

// // Route to Generate JWT Token (For Testing Purposes)
// app.post('/api/login', (req, res) => {
// const { username, email } = req.body;

// // Generate a JWT token
// const token = jwt.sign(
// { name: username, email: email },
// process.env.JWT_SECRET,
// { expiresIn: '1h' }
// );

// res.json({ token });
// });
app.post('/api/login', (req, res) => {
const { username, email } = req.body;
const token = jwt.sign(
{ name: username, email: email },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);

res.json({ token });
});

let lrs;
try {
lrs = new TinCan.LRS({
endpoint: process.env.LRS_ENDPOINT,
username: process.env.LRS_KEY,
password: process.env.LRS_SECRET,
allowFail: false
});
console.log("LRS object setup successfully.");
} catch (ex) {
console.error("Failed to setup LRS object: ", ex);
process.exit(1);
}


app.post('/api/video-metadata', authenticateJWT, (req, res) => {
const { videoId, title, description, duration, format } = req.body;

if (!videoId || !title || !duration || !format) {
console.error("Missing video metadata in request body");
return res.status(400).send("Missing video metadata in request body");
}


console.log("Received video metadata:", { videoId, title, description, duration, format });

const supportedFormats = ['mp4', 'avi', 'mov'];
if (!supportedFormats.includes(format)) {
console.error(`Unsupported video format: ${format}`);
return res.status(400).send(`Unsupported video format: ${format}`);
}

const statement = new TinCan.Statement({
actor: {
mbox: `mailto:${req.user.email}`,
name: req.user.name,
},
verb: {
id: "http://adlnet.gov/expapi/verbs/experienced",
display: { "en-US": "experienced" }
},
object: {
id: `http://example.com/videos/${videoId}`,
definition: {
name: { "en-US": title },
description: { "en-US": description }
}
},
result: {
duration: `PT${Math.floor(duration / 60)}M${duration % 60}S`
},
context: {
extensions: {
"http://example.com/metadata/format": format
}
}
});

console.log("Attempting to save video metadata statement:", statement);


lrs.saveStatement(statement, {
callback: function (err, xhr) {
if (err !== null) {
console.error("Failed to save statement:", err);
console.error("Error details:", xhr ? xhr.responseText : "No response text");
res.status(500).send("Failed to save video metadata");
} else {
console.log("Video metadata saved successfully to SCORM Cloud");
res.status(200).send("Video metadata saved successfully");
}
}
});
});

app.use((err, req, res, next) => {
console.error("An error occurred:", err.message);
res.status(500).send("Internal Server Error");
});

app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});

11 changes: 11 additions & 0 deletions Video_Player/Backend/models/Quiz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const mongoose = require('mongoose');

const QuizSchema = new mongoose.Schema({
question: String,
type: { type: String, enum: ['multiple-choice', 'fill-in-the-blank'], required: true },
options: [String], // Only for multiple-choice questions
correctAnswer: String, // Correct option or answer
points: { type: Number, default: 4 }
});

const Quiz = mongoose.model('Quiz', QuizSchema);
Loading

0 comments on commit cce14ef

Please sign in to comment.