Skip to content

Commit

Permalink
Merge branch 'replace-api-request' into fetch-cache
Browse files Browse the repository at this point in the history
  • Loading branch information
TyHil committed Jan 27, 2024
2 parents fc1770e + 26d024f commit 4c991fb
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 30 deletions.
1 change: 1 addition & 0 deletions src/data/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export const PROFESSOR_QUERY = {
};

export const SCHOOL_ID = '1273';
export const RMP_GRAPHQL_URL = 'https://www.ratemyprofessors.com/graphql';
123 changes: 93 additions & 30 deletions src/data/fetchFromRmp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { HEADERS, PROFESSOR_QUERY } from '~data/config';
import { HEADERS, PROFESSOR_QUERY, RMP_GRAPHQL_URL } from '~data/config';
import fetchWithCache, { cacheIndexRmp } from '~data/fetchWithCache';

function reportError(context, err) {
console.error('Error in ' + context + ': ' + err);
}

function getProfessorUrl(professorName: string, schoolId: string): string {
const url = new URL(
'https://www.ratemyprofessors.com/search/professors/' + schoolId + '?',
Expand All @@ -10,24 +14,37 @@ function getProfessorUrl(professorName: string, schoolId: string): string {
}

function getProfessorId(text: string, professorName: string): string {
let professorId = '';
const lowerCaseProfessorName = professorName.toLowerCase();

let matched = false;
const regex = /"legacyId":(\d+).*?"firstName":"(.*?)","lastName":"(.*?)"/g;
for (const match of text.matchAll(regex)) {
if (
lowerCaseProfessorName.includes(
match[2].split(' ')[0].toLowerCase() + ' ' + match[3].toLowerCase(),
)
) {
professorId = match[1];
matched = true;
let pendingMatch = null;
const regex =
/"legacyId":(\d+).*?"numRatings":(\d+).*?"firstName":"(.*?)","lastName":"(.*?)"/g;
const allMatches: string[] = text.match(regex);
const highestNumRatings = 0;

if (allMatches) {
for (const fullMatch of allMatches) {
for (const match of fullMatch.matchAll(regex)) {
console.log(
match[3].split(' ')[0].toLowerCase() +
' ' +
match[4].toLowerCase() +
' ',
);
const numRatings = parseInt(match[2]);
if (
lowerCaseProfessorName.includes(
match[3].split(' ')[0].toLowerCase() + ' ' + match[4].toLowerCase(),
) &&
numRatings >= highestNumRatings
) {
pendingMatch = match[1];
}
}
}
}
if (!matched) professorId = null;

return professorId;
return pendingMatch;
}

function getGraphQlUrlProp(professorId: string) {
Expand All @@ -42,23 +59,68 @@ function getGraphQlUrlProp(professorId: string) {
};
}

function wait(delay) {
return new Promise((resolve) => setTimeout(resolve, delay));
}

function fetchRetry(url: string, delay: number, tries: number, fetchOptions) {
function onError(err) {
const triesLeft: number = tries - 1;
if (!triesLeft) {
throw err;
}
return wait(delay).then(() =>
fetchRetry(url, delay, triesLeft, fetchOptions),
);
}
return fetchWithCache(url, fetchOptions, cacheIndexRmp, 2629800000).catch(
onError,
);
}

async function validateResponse(response, fetchOptions) {
const notOk = response?.status !== 200;
if (notOk && response && response.url) {
const details = {
status: response.status,
statusText: response.statusText,
redirected: response.redirected,
url: response.url,
};
reportError(
'validateResponse',
'Status not OK for fetch request. Details are: ' +
JSON.stringify(details),
);
// If we don't have fetch options, we just use an empty object.
response = await fetchRetry(response?.url, 200, 3, fetchOptions || {});
}
return response;
}

function fetchWithGraphQl(graphQlUrlProp, resolve) {
const graphqlUrl = 'https://www.ratemyprofessors.com/graphql';

fetchWithCache(graphqlUrl, graphQlUrlProp, cacheIndexRmp, 2629800000)
.then((rating) => {
if (
rating != null &&
Object.hasOwn(rating, 'data') &&
Object.hasOwn(rating['data'], 'node')
) {
rating = rating['data']['node'];
}
resolve(rating);
})
.catch((error) => {
console.error('RMP', error);
});
try {
fetchWithCache(
RMP_GRAPHQL_URL,
graphQlUrlProp,
cacheIndexRmp,
2629800000,
).then((response) =>
validateResponse(response, graphQlUrlProp).then((rating) => {
if (
rating != null &&
Object.hasOwn(rating, 'data') &&
Object.hasOwn(rating['data'], 'node')
) {
rating = rating['data']['node'];
}
resolve(rating);
}),
);
} catch (err) {
reportError('fetchWithGraphQl', err);
resolve(null); ///
}
}

export interface RmpRequest {
Expand Down Expand Up @@ -93,6 +155,7 @@ export function requestProfessorFromRmp(
fetchWithGraphQl(graphQlUrlProp, resolve);
})
.catch((error) => {
reportError('requestProfessorFromRmp', error);
reject(error);
});
});
Expand Down
129 changes: 129 additions & 0 deletions src/data/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//TODO: Fix these any types

export interface FetchProfessorParameters {
firstName: string;
lastName: string;
}

export interface FetchCourseParameters {
subjectPrefix: string;
courseNumber: string;
}

export interface FetchSectionParameters {
courseReference: string;
professorReference: string;
}

interface Requisites {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options: any[];
required: number;
type: string;
}

export interface CourseInterface {
__v: number;
_id: string;
activity_type: string;
class_level: string;
co_or_pre_requisites: Requisites;
corequisites: Requisites;
course_number: string;
credit_hours: string;
description: string;
grading: string;
internal_course_number: string;
laboratory_contact_hours: string;
lecture_contact_hours: string;
offering_frequency: string;
prerequisites: Requisites;
school: string;
sections: string[];
subject_prefix: string;
title: string;
}

interface Office {
building: string;
room: string;
map_uri: string;
}

export interface ProfessorInterface {
__v: number;
_id: string;
email: string;
first_name: string;
image_uri: string;
last_name: string;
office: Office;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
office_hours: any[];
phone_number: string;
profile_uri: string;
sections: string[];
titles: string[];
}

export interface SectionInterface {
__v: number;
_id: string;
academic_session: {
end_date: string;
name: string;
start_date: string;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
attributes: any[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
core_flags: any[];
course_reference: string;
grade_distribution: number[];
instruction_mode: string;
internal_class_number: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
meetings: any[];
professors: string[];
section_corequisites: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
options: any[];
type: string;
};
section_number: string;
syllabus_uri: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
teaching_assistants: any[];
}

export interface CourseCodeInterface {
courseCount: number;
courseName: string;
}
export interface RatingsDistributionInterface {
r1: number;
r2: number;
r3: number;
r4: number;
r5: number;
total: number;
}
export interface TeacherRatingTag {
tagCount: number;
tagName: string;
}

export interface RMPRatingInterface {
avgDifficulty: number;
avgRating: number;
courseCodes: CourseCodeInterface[];
department: string;
firstName: string;
lastName: string;
legacyId: number;
numRatings: number;
ratingsDistribution: RatingsDistributionInterface;
school: { id: string };
teacherRatingTags: TeacherRatingTag[];
wouldTakeAgainPercent: number;
}

0 comments on commit 4c991fb

Please sign in to comment.