Skip to content

Commit

Permalink
add functions and routes for ai: including new function for getting r…
Browse files Browse the repository at this point in the history
…eviews rather than just modifying function in course
  • Loading branch information
leihelen committed May 6, 2024
1 parent 4bd8db1 commit 0f36e4d
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 15 deletions.
101 changes: 101 additions & 0 deletions server/src/ai/ai.functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import dotenv from 'dotenv';
import OpenAI from "openai";
import { Reviews } from "../../db/schema";
import { findCourseById } from '../course/course.data-access';
import { CourseIdRequestType } from '../course/course.type';
import { findReviewCrossListOR } from '../utils';

dotenv.config();
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});

/** Docstring for makeSummary. Takes in all reviews from a course as text and
* creates a 50 word summary of those reviews.
* @params all reviews from a course
* @returns summary of reviews
*/
async function makeSummary(text: string) {
const completion = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [
{ role: "system", content: "You are creating a 50 word summary based on the collection of course reviews provided." },
{ role: "user", content: text }
],
});
return completion.choices[0].message.content;
}

/** Docstring for getCoursesWithMinReviews. We only want to create summaries for
* courses that have at least a certain number of reviews, so takes in that number
* and returns the courses that have at least that number of reviews.
* @params min reviews summary should be created for
* @returns courses ids with at least min reviews
*/
async function getCoursesWithMinReviews(minimum) {
const courses = await Reviews.aggregate([
{
$group: {
_id: "$class",
reviewCount: { $sum: 1 }
}
},
{ $match: { reviewCount: { $gte: minimum } } },
{
$project: {
_id: 0,
classId: "$_id"
}
}
]);

const courseIds = courses.map(course => course.classId);
return courseIds;
}

/**
* DocString for getReviewsForSummary. Gets all reviews from a course that will be used
* to generate the summary for a course
* @param params takes in the courseID of a course that we need to generate a summary for
* @returns all reviews for that course concatenated into a single string
*/
async function getReviewsForSummary(params: CourseIdRequestType) {
const { courseId } = params;

// Validate the course ID format
const regex = new RegExp(/^(?=.*[A-Z0-9])/i);
if (!regex.test(courseId)) {
return null; // Return null for invalid course ID
}

// Fetch the course by ID
const course = await findCourseById(courseId);
if (!course) return null; // Return null if no course is found

// Fetch reviews for the course, including cross-listed courses
const crossListOR = getCrossListOR(course);
if (!crossListOR) return null; // Return null if no cross-listing data is available

const reviews = await findReviewCrossListOR(crossListOR);
if (reviews.length === 0) return null; // Return null if no reviews are found

// Create a single string of all review texts separated by '/'
const reviewTexts = reviews.map(review => review.text || "No review text").join(' / ');

return reviewTexts;
}


/**
* Helper function to get crosslist OR search criteria from a course.
* @param course The course object to extract crosslist data.
* @returns The formatted search criteria for crosslist.
*/
const getCrossListOR = (course) => {
if (!course || !course.crossList || course.crossList.length === 0) {
return null;
}
return [...course.crossList.map(cID => ({ class: cID })), { class: course._id }];
};

export { makeSummary, getCoursesWithMinReviews, getReviewsForSummary }
48 changes: 48 additions & 0 deletions server/src/ai/ai.router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import express from 'express';
import { makeSummary, getCoursesWithMinReviews } from './ai.functions';

const aiRouter = express.Router();

aiRouter.use(express.json());

/** Reachable at POST /api/summarizeReviews
* @body a block of text containing all reviews from a course
* returns a summary created by OpenAI
*/
aiRouter.post('/summarizeReviews', async (req, res) => {
try {
if (!req.body.text) {
return res.status(400).json({ error: 'No text provided' });
}
const summary = await makeSummary(req.body.text);
res.status(200).json({ summary });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal Server Error' });
}
});

/** Reachable at POST /api/getCourseIdsForSummary
* @body minimum number of reviews needed to create a summary
* returns all course ids that have at least that number of reviews
*/
aiRouter.post('/getCourseIdsForSummary', async (req, res) => {
try {
const min = req.body.min;
const ids = await getCoursesWithMinReviews(min);
if (ids === null) {
return res.status(400).json({
error: `No courses found with given minimum number of reviews`,
});
}

return res.status(200).json({
message: 'Retrieved all courses',
ids: ids,
});
} catch (err) {
return res.status(500).json({ error: `Internal Server Error: ${err}` });
}
});

export default aiRouter;
Loading

0 comments on commit 0f36e4d

Please sign in to comment.