180 lines
6.1 KiB
TypeScript
180 lines
6.1 KiB
TypeScript
import express from "express";
|
|
import path from "path";
|
|
import { createServer as createViteServer } from "vite";
|
|
|
|
async function startServer() {
|
|
const app = express();
|
|
const PORT = 3000;
|
|
|
|
app.use(express.json());
|
|
|
|
// In-memory "database"
|
|
const db = {
|
|
users: [
|
|
{ id: "admin1", username: "admin", password: "password", role: "admin", name: "Administrator" },
|
|
{ id: "teacher1", username: "teacher1", password: "password", role: "teacher", name: "Gokula Krishnan", assignedGrade: "12" },
|
|
{ id: "teacher2", username: "teacher2", password: "password", role: "teacher", name: "Arun Kumar", assignedGrade: "10" },
|
|
{ id: "teacher3", username: "teacher3", password: "password", role: "teacher", name: "Meera Devi", assignedGrade: "11" },
|
|
{ id: "teacher4", username: "teacher4", password: "password", role: "teacher", name: "Senthil Nathan", assignedGrade: "9" }
|
|
],
|
|
submissions: [] as any[],
|
|
students: [] as any[],
|
|
studentStats: {} as Record<string, any> // rollNumber -> latest LEI stats
|
|
};
|
|
|
|
// Auth
|
|
app.post("/api/login", (req, res) => {
|
|
const { username, password } = req.body;
|
|
const user = db.users.find(u => u.username === username && u.password === password);
|
|
if (user) {
|
|
res.json({ id: user.id, username: user.username, role: user.role, name: user.name, assignedGrade: (user as any).assignedGrade });
|
|
} else {
|
|
res.status(401).json({ success: false, message: "Invalid credentials" });
|
|
}
|
|
});
|
|
|
|
// Submissions
|
|
app.post("/api/submissions", (req, res) => {
|
|
const submission = {
|
|
id: Date.now().toString(),
|
|
...req.body,
|
|
status: "pending",
|
|
submittedAt: new Date().toISOString(),
|
|
documentationMarks: 0,
|
|
vivaScore: 0,
|
|
understandingLevel: 0,
|
|
explanationAbility: 0,
|
|
teacherRemarks: "",
|
|
lei: req.body.lei || 0,
|
|
leiCategory: req.body.leiCategory || 'Stalled Learner',
|
|
leiInsight: req.body.leiInsight || '',
|
|
retries: req.body.retries || 0,
|
|
streak: req.body.streak || 0,
|
|
completionRate: req.body.completionRate || 0
|
|
};
|
|
db.submissions.push(submission);
|
|
res.json({ success: true, submission });
|
|
});
|
|
|
|
app.get("/api/submissions", (req, res) => {
|
|
const { grade } = req.query;
|
|
let filtered = db.submissions;
|
|
if (grade) {
|
|
filtered = filtered.filter(s => s.grade.includes(grade as string));
|
|
}
|
|
res.json(filtered);
|
|
});
|
|
|
|
app.post("/api/student/lei", (req, res) => {
|
|
const { rollNumber, lei, leiCategory, leiInsight, studentName, grade } = req.body;
|
|
if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" });
|
|
|
|
db.studentStats[rollNumber] = {
|
|
lei,
|
|
leiCategory,
|
|
leiInsight,
|
|
studentName,
|
|
grade,
|
|
updatedAt: new Date().toISOString()
|
|
};
|
|
|
|
res.json({ success: true });
|
|
});
|
|
|
|
app.get("/api/students/lei", (req, res) => {
|
|
const { grade } = req.query;
|
|
let stats = Object.values(db.studentStats);
|
|
if (grade) {
|
|
stats = stats.filter(s => s.grade.includes(grade as string));
|
|
}
|
|
res.json(stats);
|
|
});
|
|
|
|
app.post("/api/student/viva", (req, res) => {
|
|
const { rollNumber, session, vivaScore } = req.body;
|
|
if (!rollNumber) return res.status(400).json({ success: false, message: "Roll number required" });
|
|
|
|
if (db.studentStats[rollNumber]) {
|
|
if (!db.studentStats[rollNumber].vivaSessions) {
|
|
db.studentStats[rollNumber].vivaSessions = [];
|
|
}
|
|
db.studentStats[rollNumber].vivaSessions.push(session);
|
|
db.studentStats[rollNumber].vivaSummary = session.summary;
|
|
db.studentStats[rollNumber].vivaScore = vivaScore;
|
|
}
|
|
|
|
res.json({ success: true });
|
|
});
|
|
|
|
app.patch("/api/submissions/:id", (req, res) => {
|
|
const { id } = req.params;
|
|
const {
|
|
documentationMarks, vivaScore, teacherVivaMarks, vivaRequested,
|
|
teacherRemarks, status, marks, totalScore
|
|
} = req.body;
|
|
const index = db.submissions.findIndex(s => s.id === id);
|
|
if (index !== -1) {
|
|
if (documentationMarks !== undefined) db.submissions[index].documentationMarks = documentationMarks;
|
|
if (vivaScore !== undefined) db.submissions[index].vivaScore = vivaScore;
|
|
if (teacherVivaMarks !== undefined) db.submissions[index].teacherVivaMarks = teacherVivaMarks;
|
|
if (vivaRequested !== undefined) db.submissions[index].vivaRequested = vivaRequested;
|
|
if (teacherRemarks !== undefined) db.submissions[index].teacherRemarks = teacherRemarks;
|
|
if (marks !== undefined) db.submissions[index].marks = marks;
|
|
if (totalScore !== undefined) db.submissions[index].totalScore = totalScore;
|
|
db.submissions[index].status = status || "reviewed";
|
|
|
|
res.json({ success: true, submission: db.submissions[index] });
|
|
} else {
|
|
res.status(404).json({ success: false, message: "Submission not found" });
|
|
}
|
|
});
|
|
|
|
// Leaderboard
|
|
app.get("/api/leaderboard", (req, res) => {
|
|
const { grade } = req.query;
|
|
let filtered = db.submissions;
|
|
if (grade) {
|
|
filtered = filtered.filter(s => s.grade.includes(grade as string));
|
|
}
|
|
const leaderboard = filtered
|
|
.sort((a, b) => (b.lei || 0) - (a.lei || 0))
|
|
.slice(0, 10)
|
|
.map(s => {
|
|
let label = "Active Learner";
|
|
if (s.lei > 100) label = "Top Contributor";
|
|
else if (s.lei > 50) label = "Most Consistent";
|
|
else if (s.lei > 20) label = "Rising Star";
|
|
|
|
return {
|
|
name: s.studentName,
|
|
lei: s.lei || 0,
|
|
grade: s.grade,
|
|
rollNumber: s.rollNumber,
|
|
label: label
|
|
};
|
|
});
|
|
res.json(leaderboard);
|
|
});
|
|
|
|
// Vite middleware for development
|
|
if (process.env.NODE_ENV !== "production") {
|
|
const vite = await createViteServer({
|
|
server: { middlewareMode: true },
|
|
appType: "spa",
|
|
});
|
|
app.use(vite.middlewares);
|
|
} else {
|
|
const distPath = path.join(process.cwd(), 'dist');
|
|
app.use(express.static(distPath));
|
|
app.get('*', (req, res) => {
|
|
res.sendFile(path.join(distPath, 'index.html'));
|
|
});
|
|
}
|
|
|
|
app.listen(PORT, "0.0.0.0", () => {
|
|
console.log(`Server running on http://localhost:${PORT}`);
|
|
});
|
|
}
|
|
|
|
startServer();
|