Skip to content

Commit

Permalink
feat: Bookmarked classes and courses
Browse files Browse the repository at this point in the history
  • Loading branch information
mathhulk committed Oct 17, 2024
1 parent 4f22d96 commit 6d2d30d
Show file tree
Hide file tree
Showing 21 changed files with 817 additions and 384 deletions.
6 changes: 3 additions & 3 deletions apps/backend/src/bootstrap/loaders/passport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,17 @@ export default async (app: Application, redis: RedisClientType) => {
const email = profile.emails?.[0].value;

if (!email) {
return done(null, false, { message: "No email found" });
return done(null, false, { message: "Invalid" });
}

let user = await UserModel.findOne({ email });

if (!user) {
user = new UserModel({
email,
google_id: profile.id,
googleId: profile.id,
name: profile.displayName,
// refresh_token: refreshToken, <-------------- currently not needed.
// TODO: refreshToken
});
}

Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/modules/course/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import { IntermediateCourse, formatCourse } from "./formatter";

export const getCourse = async (
subject: string,
courseNumber: string,
number: string,
info?: GraphQLResolveInfo | null
) => {
const course = await CourseModel.findOne({
"subjectArea.code": subject,
"catalogNumber.formatted": courseNumber,
"catalogNumber.formatted": number,
})
.sort({ fromDate: -1 })
.lean();
Expand Down
33 changes: 32 additions & 1 deletion apps/backend/src/modules/schedule/controller.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { ScheduleModel, TermModel } from "@repo/common";
import { ClassModel, ScheduleModel, TermModel } from "@repo/common";

import {
CreateScheduleInput,
SelectedClassInput,
Semester,
UpdateScheduleInput,
} from "../../generated-types/graphql";
import { formatClass } from "../class/formatter";
import { ClassModule } from "../class/generated-types/module-types";
import { formatSchedule } from "./formatter";
import { ScheduleModule } from "./generated-types/module-types";

export const getSchedules = async (context: any) => {
if (!context.user._id) throw new Error("Unauthorized");
Expand Down Expand Up @@ -91,3 +96,29 @@ export const updateSchedule = async (

return await formatSchedule(schedule);
};

export const getClasses = async (
year: Number,
semester: Semester,
selectedClasses: SelectedClassInput[]
) => {
const classes = [];

for (const selectedClass of selectedClasses) {
const _class = await ClassModel.findOne({
number: selectedClass.number,
"course.subjectArea.code": selectedClass.subject,
"course.catalogNumber.formatted": selectedClass.courseNumber,
"session.term.name": `${year} ${semester}`,
}).lean();

if (!_class) continue;

classes.push({
class: formatClass(_class) as unknown as ClassModule.Class,
selectedSections: selectedClass.sections,
} as ScheduleModule.SelectedClass);
}

return classes;
};
30 changes: 7 additions & 23 deletions apps/backend/src/modules/schedule/formatter.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,22 @@
import { ClassModel, ScheduleType } from "@repo/common";
import { ScheduleType } from "@repo/common";

import { formatClass } from "../class/formatter";
import { ClassModule } from "../class/generated-types/module-types";
import { ScheduleModule } from "./generated-types/module-types";

export type IntermediateSchedule = Omit<ScheduleModule.Schedule, "term"> & {
export type IntermediateSchedule = Omit<
ScheduleModule.Schedule,
"term" | "classes"
> & {
term: null;
classes: ScheduleModule.SelectedClassInput[];
};

export const formatSchedule = async (schedule: ScheduleType) => {
const classes = [];

for (const selectedClass of schedule.classes) {
const _class = await ClassModel.findOne({
number: selectedClass.number,
"course.subjectArea.code": selectedClass.subject,
"course.catalogNumber.formatted": selectedClass.courseNumber,
"session.term.name": `${schedule.year} ${schedule.semester}`,
}).lean();

if (!_class) continue;

classes.push({
class: formatClass(_class) as unknown as ClassModule.Class,
selectedSections: selectedClass.sections,
});
}

return {
_id: schedule._id as string,
name: schedule.name,
createdBy: schedule.createdBy,
public: schedule.public,
classes,
classes: schedule.classes,
year: schedule.year,
semester: schedule.semester,
term: null,
Expand Down
17 changes: 17 additions & 0 deletions apps/backend/src/modules/schedule/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TermModule } from "../term/generated-types/module-types";
import {
createSchedule,
deleteSchedule,
getClasses,
getSchedule,
getSchedules,
updateSchedule,
Expand Down Expand Up @@ -34,6 +35,22 @@ const resolvers: ScheduleModule.Resolvers = {

return term as unknown as TermModule.Term;
},

classes: async (parent: IntermediateSchedule | ScheduleModule.Schedule) => {
if (
parent.classes[0] &&
(parent.classes[0] as ScheduleModule.SelectedClass).class
)
return parent.classes as ScheduleModule.SelectedClass[];

const classes = await getClasses(
parent.year,
parent.semester as Semester,
parent.classes as ScheduleModule.SelectedClassInput[]
);

return classes;
},
},

Mutation: {
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/modules/schedule/typedefs/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { gql } from "graphql-tag";
const typedef = gql`
type SelectedClass {
class: Class!
selectedSections: [Int!]
selectedSections: [String!]
}
type Event {
Expand Down Expand Up @@ -45,7 +45,7 @@ const typedef = gql`
subject: String!
courseNumber: String!
number: String!
sections: [Int!]!
sections: [String!]!
}
input UpdateScheduleInput {
Expand Down
62 changes: 61 additions & 1 deletion apps/backend/src/modules/user/controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { UserModel } from "@repo/common";
import { ClassModel, CourseModel, UserModel } from "@repo/common";

import { UpdateUserInput } from "../../generated-types/graphql";
import { formatClass } from "../class/formatter";
import { formatCourse } from "../course/formatter";
import { formatUser } from "./formatter";
import { UserModule } from "./generated-types/module-types";

export const getUser = async (context: any) => {
if (!context.user._id) throw new Error("Unauthorized");
Expand All @@ -11,3 +15,59 @@ export const getUser = async (context: any) => {

return formatUser(user);
};

export const updateUser = async (context: any, user: UpdateUserInput) => {
if (!context.user._id) throw new Error("Unauthorized");

const updatedUser = await UserModel.findByIdAndUpdate(
context.user._id,
user,
{ new: true }
);

if (!updatedUser) throw new Error("Invalid");

return formatUser(updatedUser);
};

export const getBookmarkedCourses = async (
bookmarkedCourses: UserModule.BookmarkedCourseInput[]
) => {
const courses = [];

for (const bookmarkedCourse of bookmarkedCourses) {
const course = await CourseModel.findOne({
"subjectArea.code": bookmarkedCourse.subject,
"catalogNumber.formatted": bookmarkedCourse.number,
})
.sort({ fromDate: -1 })
.lean();

if (!course) continue;

courses.push(course);
}

return courses.map(formatCourse);
};

export const getBookmarkedClasses = async (
bookmarkedClasses: UserModule.BookmarkedClassInput[]
) => {
const classes = [];

for (const bookmarkedClass of bookmarkedClasses) {
const _class = await ClassModel.findOne({
number: bookmarkedClass.number,
"course.subjectArea.code": bookmarkedClass.subject,
"course.catalogNumber.formatted": bookmarkedClass.courseNumber,
"session.term.name": `${bookmarkedClass.year} ${bookmarkedClass.semester}`,
}).lean();

if (!_class) continue;

classes.push(_class);
}

return classes.map(formatClass);
};
12 changes: 11 additions & 1 deletion apps/backend/src/modules/user/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ import { UserType } from "@repo/common";

import { UserModule } from "./generated-types/module-types";

export type IntermediateUser = Omit<
UserModule.User,
"bookmarkedClasses" | "bookmarkedCourses"
> & {
bookmarkedCourses: UserModule.BookmarkedCourseInput[];
bookmarkedClasses: UserModule.BookmarkedClassInput[];
};

export const formatUser = (user: UserType) => {
return {
email: user.email,
student: user.email.endsWith("@berkeley.edu"),
} as UserModule.User;
bookmarkedCourses: user.bookmarkedCourses,
bookmarkedClasses: user.bookmarkedClasses,
} as IntermediateUser;
};
48 changes: 45 additions & 3 deletions apps/backend/src/modules/user/resolver.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,52 @@
import { getUser } from "./controller";
import {
getBookmarkedClasses,
getBookmarkedCourses,
getUser,
updateUser,
} from "./controller";
import { IntermediateUser } from "./formatter";
import { UserModule } from "./generated-types/module-types";

const resolvers: UserModule.Resolvers = {
Query: {
user(_, __, context) {
return getUser(context);
user: async (_, __, context) => {
const user = await getUser(context);

return user as unknown as UserModule.User;
},
},

User: {
bookmarkedClasses: async (parent: UserModule.User | IntermediateUser) => {
if (
parent.bookmarkedClasses[0] &&
(parent.bookmarkedClasses[0] as UserModule.Class).title
)
return parent.bookmarkedClasses as UserModule.Class[];

const classes = await getBookmarkedClasses(parent.bookmarkedClasses);

return classes as unknown as UserModule.Class[];
},

bookmarkedCourses: async (parent: UserModule.User | IntermediateUser) => {
if (
parent.bookmarkedCourses[0] &&
(parent.bookmarkedCourses[0] as UserModule.Course).title
)
return parent.bookmarkedCourses as UserModule.Course[];

const courses = await getBookmarkedCourses(parent.bookmarkedCourses);

return courses as unknown as UserModule.Course[];
},
},

Mutation: {
updateUser: async (_, { user: input }, context) => {
const user = await updateUser(context, input);

return user as unknown as UserModule.User;
},
},
};
Expand Down
24 changes: 24 additions & 0 deletions apps/backend/src/modules/user/typedefs/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,35 @@ const typedef = gql`
type User {
email: String!
student: Boolean!
bookmarkedCourses: [Course!]!
bookmarkedClasses: [Class!]!
}
type Query {
user: User @auth
}
input BookmarkedCourseInput {
subject: String!
number: String!
}
input BookmarkedClassInput {
year: Int!
semester: Semester!
subject: String!
courseNumber: String!
number: String!
}
input UpdateUserInput {
bookmarkedClasses: [BookmarkedClassInput!]
bookmarkedCourses: [BookmarkedCourseInput!]
}
type Mutation {
updateUser(user: UpdateUserInput!): User @auth
}
`;

export default typedef;
Loading

0 comments on commit 6d2d30d

Please sign in to comment.