feat: Implement the tracker

This commit is contained in:
Alexander Polynomdivision 2018-10-09 16:01:17 +02:00
parent d5c4563c77
commit c622264f43
5 changed files with 132 additions and 0 deletions

View File

@ -19,6 +19,8 @@ import UserRouter from "./api/user";
import ClassRouter from "./api/class"; import ClassRouter from "./api/class";
import LevelRouter from "./api/level"; import LevelRouter from "./api/level";
import { ITrackerDBModel } from "./models/tracker";
const baseRouter = express.Router(); const baseRouter = express.Router();
const authRouter = express.Router(); const authRouter = express.Router();
@ -66,6 +68,40 @@ const password = encodeURIComponent(env["LATEINICUS_USER_PW"]);
app.use("/api/level", LevelRouter); app.use("/api/level", LevelRouter);
app.use("/api/class", ClassRouter); app.use("/api/class", ClassRouter);
app.use("/api/user", UserRouter); app.use("/api/user", UserRouter);
app.post("/api/tracker", async (req, res) => {
// Did we get any data
if (!req.body) {
res.send({
error: "403",
data: {
msg: "No request body provided",
},
});
return;
}
// Did we get all arguments?
if (!("session" in req.body) || !("event" in req.body)) {
res.send({
error: "403",
data: {
msg: "Invalid request",
},
});
return;
}
// Insert it into the database
const tracker: ITrackerDBModel = Object.assign({}, req.body, {
timestamp: Date.now(),
});
await db.collection("tracker").insertOne(tracker);
res.send({
error: "200",
data: {},
});
});
app.get("/api/levels", async (req, res) => { app.get("/api/levels", async (req, res) => {
// TODO: if (levels) // TODO: if (levels)
const levels = (await db.collection("levels").find({}, { const levels = (await db.collection("levels").find({}, {

View File

@ -0,0 +1,16 @@
export enum TrackerEvent {
LOG_IN = "LOG_IN",
LOG_OUT = "LOG_OUT",
START_LEARNING = "START_LEARNING",
CANCEL_LEARNING = "CANCEL_LEARNING",
FINISH_LEARNING = "FINISH_LEARNING",
};
export interface ITrackerRequest {
session: string;
event: TrackerEvent;
};
export type ITrackerDBModel = ITrackerRequest & {
timestamp: number;
};

View File

@ -54,6 +54,19 @@ export default class Application extends React.Component<IProps> {
} }
checkAuthStatus = (token: string): Promise<IUser> => { checkAuthStatus = (token: string): Promise<IUser> => {
// Track the end of a review
console.log("Sending trcaker request");
fetch(`${BACKEND_URL}/api/tracker`, {
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
body: JSON.stringify({
session: window.sessionStorage.getItem("tracker_session"),
event: "LOG_IN",
}),
});
return new Promise((res, rej) => { return new Promise((res, rej) => {
fetch(`${BACKEND_URL}/api/user/me`, { fetch(`${BACKEND_URL}/api/user/me`, {
headers: new Headers({ headers: new Headers({
@ -128,6 +141,18 @@ export default class Application extends React.Component<IProps> {
}).then(resp => resp.json(), err => { }).then(resp => resp.json(), err => {
console.log("Application::setLastReview: POSTing last results failed"); console.log("Application::setLastReview: POSTing last results failed");
}); });
// Track the end of a review
fetch(`${BACKEND_URL}/api/tracker`, {
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
body: JSON.stringify({
session: window.sessionStorage.getItem("tracker_session"),
event: "FINISH_LEARNING",
}),
});
} }
getReviewQueue = (): Promise<IVocab[]> => { getReviewQueue = (): Promise<IVocab[]> => {
@ -256,6 +281,18 @@ export default class Application extends React.Component<IProps> {
} }
login = (username: string, password: string): Promise<IUser | IResponse> => { login = (username: string, password: string): Promise<IUser | IResponse> => {
// Track the login
fetch(`${BACKEND_URL}/api/tracker`, {
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
body: JSON.stringify({
session: window.sessionStorage.getItem("tracker_session"),
event: "LOG_IN",
}),
});
return new Promise((res, rej) => { return new Promise((res, rej) => {
fetch(`${BACKEND_URL}/api/login`, { fetch(`${BACKEND_URL}/api/login`, {
method: "POST", method: "POST",
@ -288,6 +325,18 @@ export default class Application extends React.Component<IProps> {
} }
logout = () => { logout = () => {
// Track the logout
fetch(`${BACKEND_URL}/api/tracker`, {
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
body: JSON.stringify({
session: window.sessionStorage.getItem("tracker_session"),
event: "LOG_OUT",
}),
});
// NOTE: No promise, since we don't care about the result // NOTE: No promise, since we don't care about the result
fetch(`${BACKEND_URL}/api/user/logout`, { fetch(`${BACKEND_URL}/api/user/logout`, {
headers: new Headers({ headers: new Headers({

View File

@ -10,6 +10,11 @@ import Application from "./containers/Application";
const store = createStore(LateinicusApp); const store = createStore(LateinicusApp);
// Generate a tracker session
let array = new Uint32Array(1);
window.crypto.getRandomValues(array);
window.sessionStorage.setItem("tracker_session", array[0].toString());
ReactDOM.render(( ReactDOM.render((
<Provider store={store}> <Provider store={store}>
<Application /> <Application />

View File

@ -34,6 +34,8 @@ import {
REVIEW_HELP_MOD REVIEW_HELP_MOD
} from "../config"; } from "../config";
import { BACKEND_URL } from "../config";
import { Queue } from "../utils/queue"; import { Queue } from "../utils/queue";
interface IProps { interface IProps {
@ -103,6 +105,18 @@ const ReviewPageWithRouter = withRouter(
[ReviewType.QUEUE]: () => vocabByQueueW(), [ReviewType.QUEUE]: () => vocabByQueueW(),
}[reviewType]; }[reviewType];
// Track the start of a session
fetch(`${BACKEND_URL}/api/tracker`, {
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
body: JSON.stringify({
session: window.sessionStorage.getItem("tracker_session"),
event: "START_LEARNING",
}),
});
getVocab().then((res: IVocab[]) => { getVocab().then((res: IVocab[]) => {
// Check if we received any vocabulary // Check if we received any vocabulary
if (res.length === 0) { if (res.length === 0) {
@ -145,6 +159,18 @@ const ReviewPageWithRouter = withRouter(
cancelReview = () => { cancelReview = () => {
this.closeDialog(); this.closeDialog();
// Track the cancellation of a learning session
fetch(`${BACKEND_URL}/api/tracker`, {
headers: new Headers({
"Content-Type": "application/json",
}),
method: "POST",
body: JSON.stringify({
session: window.sessionStorage.getItem("tracker_session"),
event: "CANCEL_LEARNING",
}),
});
// Show the drawer button again // Show the drawer button again
this.props.drawerButtonState(true); this.props.drawerButtonState(true);