feat: Implement the tracker
This commit is contained in:
parent
d5c4563c77
commit
c622264f43
@ -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({}, {
|
||||||
|
16
backend/src/models/tracker.ts
Normal file
16
backend/src/models/tracker.ts
Normal 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;
|
||||||
|
};
|
@ -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({
|
||||||
|
@ -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 />
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user