Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: cd client && yarn --frozen-lockfile && yarn lint
lint_server:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: cd server && yarn --frozen-lockfile && yarn lint
3 changes: 3 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

cd client
npx lint-staged

cd ../server
yarn lint
3 changes: 3 additions & 0 deletions server/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
.idea
25 changes: 25 additions & 0 deletions server/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"env": {
"node": true
},
"extends": [
"standard",
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 13,
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
"rules": {
"no-use-before-define": "off",
"no-unused-vars": "warn",
"no-console": "off",
"camelcase": "off",
"no-throw-literal": "warn"
},
"settings": {
}
}
5 changes: 4 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"prestart": "tsc",
"start": "node .",
"dev": "nodemon --watch src -e ts,ejs --exec yarn run start",
"build": "tsc --project ./"
"build": "tsc --project ./",
"lint": "eslint --ext .ts .",
"lint:fix": "eslint --ext .ts --fix ."
},
"repository": "git+https://github.com/hiimchrislim/QuizVotingSystem.git",
"author": "Chris, Akshit, Shubh, Ilir",
Expand Down Expand Up @@ -42,6 +44,7 @@
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.1",
"husky": "^8.0.1",
"nodemon": "^2.0.14",
"prettier": "^2.4.1",
"socket.io-client": "^4.3.2",
Expand Down
164 changes: 81 additions & 83 deletions server/src/controllers/pollController.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,77 @@
import { Poll } from "../db/schema";
import { PollModel, StudentModel } from "../db/mogoose";
import { io } from "../socket";
import { client } from "../redis";
import { customAlphabet } from "nanoid/async";
import { pollResult } from "./socketController";
const nanoid = customAlphabet("qwertyuiopasdfghjklzxcvbnm1234567890", 6);
import { Poll } from '../db/schema'
import { PollModel, StudentModel } from '../db/mogoose'
import { io } from '../socket'
import { client } from '../redis'
import { customAlphabet } from 'nanoid/async'
import { pollResult } from './socketController'
const nanoid = customAlphabet('qwertyuiopasdfghjklzxcvbnm1234567890', 6)

// set poll code expiry to 1 day
const expiry = 60 * 60 * 24;
const expiry = 60 * 60 * 24

async function createPoll(poll: Poll) {
if (!poll.hasOwnProperty("courseCode"))
return { status: 400, data: { message: "courseCode is required" } };
const promise = [nanoid(), PollModel.create(poll)] as const;
const result = await Promise.all(promise);
const pollId = result[1]["_id"].toString();
const pollCode = result[0];
await client.set(pollCode, pollId, { EX: expiry, NX: true });
async function createPoll (poll: Poll) {
if (!poll.courseCode) { return { status: 400, data: { message: 'courseCode is required' } } }
const promise = [nanoid(), PollModel.create(poll)] as const
const result = await Promise.all(promise)
const pollId = result[1]._id.toString()
const pollCode = result[0]
await client.set(pollCode, pollId, { EX: expiry, NX: true })

return { status: 201, data: { pollCode, pollId } };
return { status: 201, data: { pollCode, pollId } }
}

async function changePollStatus(pollId: string, hasStarted: boolean) {
if (typeof hasStarted !== "boolean")
async function changePollStatus (pollId: string, hasStarted: boolean) {
if (typeof hasStarted !== 'boolean') {
return {
status: 400,
data: { message: "hasStarted should be boolean" },
};
const currSequence = await client.get(pollId);
console.log("currSequence", currSequence);
let newSequence;
data: { message: 'hasStarted should be boolean' }
}
}
const currSequence = await client.get(pollId)
console.log('currSequence', currSequence)
let newSequence
// on every new start increment the sequence counter
if (hasStarted) {
if (currSequence == null) newSequence = 0;
else newSequence = parseInt(currSequence);
if (newSequence < 0) newSequence *= -1;
newSequence++;
console.log("newSequence", newSequence);
if (currSequence == null) newSequence = 0
else newSequence = parseInt(currSequence)
if (newSequence < 0) newSequence *= -1
newSequence++
console.log('newSequence', newSequence)
await client.set(pollId, newSequence.toString(), {
EX: expiry,
});
const result = await pollResult(pollId, newSequence);
io.to(pollId).emit("result", result);
}
// for every stop make the current counter negative to indicate that it is not an active sequence
else {
EX: expiry
})
const result = await pollResult(pollId, newSequence)
io.to(pollId).emit('result', result)
} else {
// for every stop make the current counter negative to indicate that it is not an active sequence
if (currSequence != null) {
newSequence = parseInt(currSequence) * -1;
newSequence = parseInt(currSequence) * -1
await client.set(pollId, newSequence.toString(), {
EX: expiry,
});
EX: expiry
})
}
}

io.to(pollId).emit("pollStarted", hasStarted);
io.to(pollId).emit('pollStarted', hasStarted)

return { status: 200, data: { message: "poll status successfully changed" } };
return { status: 200, data: { message: 'poll status successfully changed' } }
}

async function getStudents(courseCode: string, startTime: Date, endTime: Date) {
async function getStudents (courseCode: string, startTime: Date, endTime: Date) {
// TODO Already addressed in TODO bellow
// eslint-disable-next-line no-useless-catch
try {
const pollDoc = await PollModel.find({ courseCode });
let promises: Promise<any>[] = [];
let responses: any[] = [];
const pollDoc = await PollModel.find({ courseCode })
const promises: Promise<any>[] = []
const responses: any[] = []
pollDoc.forEach((element) => {
promises.push(
StudentModel.aggregate([
{
$match: {
pollId: element._id.toString(),
timestamp: { $gte: startTime, $lte: endTime },
},
timestamp: { $gte: startTime, $lte: endTime }
}
},
{
$project: {
Expand All @@ -81,57 +82,54 @@ async function getStudents(courseCode: string, startTime: Date, endTime: Date) {
utorid: 1,
timestamp: {
$dateToString: {
date: "$timestamp",
timezone: "America/Toronto"
},
date: '$timestamp',
timezone: 'America/Toronto'
}
},
pollName: element.name,
description: element.description,
answer: 1,
},
},
answer: 1
}
}
]).then((data) => {
data.forEach((val) => {
responses.push(val);
});
responses.push(val)
})
})
);
});
await Promise.all(promises);
console.log(responses);
return { responses };
)
})
await Promise.all(promises)
console.log(responses)
return { responses }
} catch (err) {
/**
* TODO: Add error handler
*/
throw err;
throw err
}
}

async function getPollStatus(pollId: any) {
if (pollId === null || pollId === undefined || typeof pollId !== "string")
return { status: 400, data: { message: "Invalid poll Id" } };
const result = await client.get(pollId);
const pollStarted = result === null ? false : parseInt(result) > 0;
return { status: 200, data: { pollStarted } };
async function getPollStatus (pollId: any) {
if (pollId === null || pollId === undefined || typeof pollId !== 'string') { return { status: 400, data: { message: 'Invalid poll Id' } } }
const result = await client.get(pollId)
const pollStarted = result === null ? false : parseInt(result) > 0
return { status: 200, data: { pollStarted } }
}

async function getResult(pollId: any) {
if (pollId === null || pollId === undefined || typeof pollId !== "string")
return { status: 400, data: { message: "Invalid poll Id" } };
const currSequence = await client.get(pollId);
const result = await pollResult(pollId, parseInt(currSequence));
return { status: 200, data: { ...result } };
async function getResult (pollId: any) {
if (pollId === null || pollId === undefined || typeof pollId !== 'string') { return { status: 400, data: { message: 'Invalid poll Id' } } }
const currSequence = await client.get(pollId)
const result = await pollResult(pollId, parseInt(currSequence))
return { status: 200, data: { ...result } }
}

async function endForever(pollCode: string) {
if (pollCode === null || pollCode === undefined)
return { status: 400, data: { message: "Invalid poll code" } };
const pollId = await client.get(pollCode);
await Promise.all([client.del(pollCode), client.del(pollId)]);
io.to(pollId).emit("end", true);
io.of("/").in(pollId).disconnectSockets();
return { status: 200, data: { message: "Poll closed" } };
async function endForever (pollCode: string) {
if (pollCode === null || pollCode === undefined) { return { status: 400, data: { message: 'Invalid poll code' } } }
const pollId = await client.get(pollCode)
await Promise.all([client.del(pollCode), client.del(pollId)])
io.to(pollId).emit('end', true)
io.of('/').in(pollId).disconnectSockets()
return { status: 200, data: { message: 'Poll closed' } }
}

export {
Expand All @@ -140,5 +138,5 @@ export {
getStudents,
getPollStatus,
getResult,
endForever,
};
endForever
}
Loading