Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

# StressLess

[VISIT OUR DEPLOYED PRODUCT](https://stressless-frontend.onrender.com/)

_Please be patient with the deployed version as we use free tier services to deploy and it takes quite a while for things to load. Thank you!_

## Repository

<details>
<summary>Table of Contents</summary>
<ol>
Expand Down
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@types/jest": "^29.5.14",
"@types/node": "^22.13.4",
"@types/supertest": "^6.0.3",
"crypto": "^1.0.1",
"supertest": "^7.1.0",
"vitest": "^3.0.9"
},
Expand Down
94 changes: 94 additions & 0 deletions backend/src/controllers/ScheduleController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Request, Response } from 'express';
import prisma from '../config/prisma.ts';
import Scheduler from '../utils/Scheduler.ts';
import { UserPreferences } from '../db/types.ts';
import UserPreferenceUtils from '../utils/UserPreferenceUtils.ts';

/**
* ScheduleController
*
* Functions for handling triggering the backend to create a `perfect` schedule based on
* the user's current preferences, deadlines, and events. Future work might include functions
* that allow deleting all generated events, regenerating schedule, etc.
*
* How to use:
*
* User sends a HTTP request to the URL with the route that calls the below
* function. Check ../routes/user.ts for the exact routes.
*
* For example, if we want to generate a schedule:
*
* 1. From the frontend, send an HTTP POST request to the route which calls
* the generateSchedule function. For example:
*
* POST <BACKEND URL>/api/user/generate-schedule/<user-id>
*
* 2. The frontend receives the data in JSON format (JS objects/arrays).
*
* Note that we can use the Axios library in the frontend to send HTTP requests.
*/
export default class ScheduleController {
/**
* generateSchedule is a function that use the user's uuid to fetch
* user_preferences
* user_events
* user_deadlines where projected_duration and due_time are not null
* call functions from utils.Scheduler to get generated events data
* loop through result.scheduledEvents and insert into user_events database
*
* @param req
* should have user's uuid at req.params.user_id
* should not have anything in req.body
* @param res
* send back json format
*/
public static async generateSchedule(req: Request, res: Response) {
try {
// extract out the user's id
const userId = req.params.user_id;

// fetch user's preferences
const userPreferences = await prisma.user_preferences.findMany({
where: {
user_id: userId,
},
});
// transform to the processed preference type
const processedPreferences : UserPreferences = UserPreferenceUtils.transform(userPreferences);

// fetch user's events
const userEvents = await prisma.user_events.findMany({
where: {
user_id: req.params.user_id,
},
});

// fetch user's deadlines
const userDeadlines = await prisma.user_deadlines.findMany({
where: {
user_id: req.params.user_id,
projected_duration: {not: null},
due_time: {not: null},
},
});

// feed information to our scheduler logic
// TODO: what to do with unscheduledDeadlines
const {scheduledEvents} = Scheduler.scheduleDeadlines(userEvents, userDeadlines, processedPreferences);

// loop through the returned events and add them to the database
for (const event of scheduledEvents) {
// not passing id in prisma create so that prisma can handle generating uuid
const {id, ...eventDataWithoutId} = event;
await prisma.user_events.create({
data: eventDataWithoutId,
});
}

} catch (error : any) {
res
.status(500)
.json({ error: 'Error in auto-generating schedule, ' + error.message });
}
}
}
4 changes: 2 additions & 2 deletions backend/src/routes/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import DeadlineController from '../controllers/DeadlineController';
const router = Router();

// Get all events for a particular user
router.get('/events/by-user/:user', EventController.getUserEvents);
router.get('/events/by-user/:user_id', EventController.getUserEvents);

// Add an event
router.post('/events', EventController.postEvent);
Expand All @@ -21,7 +21,7 @@ router.put('/events/id/:id', EventController.putEvent);
router.delete('/events/id/:id', EventController.deleteEvent);

// Get all deadlines for a particular user
router.get('/deadlines/by-user/:user', DeadlineController.getUserDeadlines);
router.get('/deadlines/by-user/:user_id', DeadlineController.getUserDeadlines);

// Add a deadline
router.post('/deadlines', DeadlineController.postDeadline);
Expand Down
12 changes: 4 additions & 8 deletions backend/src/routes/user.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import PreferenceController from '../controllers/PreferenceController';
import { Request, Response, NextFunction } from 'express';
import ScheduleController from '../controllers/ScheduleController.ts';

// Creates the router
const express = require('express');
const router = express.Router();

// Keeps track of time on the website
const timeLog = (req: Request, res: Response, next: NextFunction) => {
console.log('Time: ', Date.now());
next();
};
router.use(timeLog);

// Save survey results to database
router.post('/surveyresults/:user_id', PreferenceController.postPreferences);
// Get all survey results from database
Expand All @@ -25,4 +18,7 @@ router.put(
PreferenceController.putPreferenceByUserId
);

// Generate events according to user's preferences and pre-existing calendar
router.post('/generate-schedule/:user_id', ScheduleController.generateSchedule);

export default router;
57 changes: 57 additions & 0 deletions backend/src/test/userPreferenceUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { UserPreference, UserPreferences } from '../db/types.ts';
import { faker } from '@faker-js/faker';
import UserPreferenceUtils from '../utils/UserPreferenceUtils.ts';

describe('Test the transform method of user preference utils', () => {
const user_id = faker.string.uuid();
const ptData : UserPreference = {
answer : "735,1095",
question_id : "pt",
user_id : user_id,
id: faker.string.uuid(),
};

const wdData : UserPreference = {
answer : "45",
question_id : "wd",
user_id : user_id,
id: faker.string.uuid(),
};

const shData : UserPreference = {
answer : "13",
question_id: "sh",
user_id: user_id,
id: faker.string.uuid(),
};

const stData : UserPreference = {
answer: "09:15",
question_id: "st",
user_id: user_id,
id: faker.string.uuid(),
};

const etData : UserPreference = {
answer: "18:07",
question_id: "et",
user_id: user_id,
id: faker.string.uuid(),
};

const fullUserPreference : UserPreference[] = [
ptData, wdData, shData, stData, stData, etData
];

const expectedUserPreferences : UserPreferences = {
productiveTime: [735,1095],
workDuration: 45,
sleepHours: 13,
startTime: 555,
endTime: 1087,
};

it('should return the correct User Preferences model data', async () => {
expect(UserPreferenceUtils.transform(fullUserPreference)).toEqual(expectedUserPreferences);
});
});
Loading