Skip to content

Commit

Permalink
Scrape courses along with professors
Browse files Browse the repository at this point in the history
  • Loading branch information
TyHil committed Dec 11, 2024
1 parent e0b939c commit 2d62f0e
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 64 deletions.
6 changes: 3 additions & 3 deletions src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { Storage } from '@plasmohq/storage';

import {
addGCalButtons,
type CourseHeader,
listenForTableChange,
scrapeCourseData,
} from '~content';
import { neededOrigins } from '~data/config';
import { type SearchQuery } from '~utils/SearchQuery';

export interface ShowCourseTabPayload {
header: CourseHeader;
professors: string[];
header: string | SearchQuery;
professors: SearchQuery[];
}

// State vars
Expand Down
45 changes: 25 additions & 20 deletions src/components/CourseOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,38 @@ import type { GenericFetchedData, GradesType } from '~pages';
import { type SearchQuery, searchQueryLabel } from '~utils/SearchQuery';

type CourseOverviewProps = {
course: SearchQuery;
grades: GenericFetchedData<GradesType>;
header: string | SearchQuery;
grades: undefined | GenericFetchedData<GradesType>;
};

const CourseOverview = ({ course, grades }: CourseOverviewProps) => {
const CourseOverview = ({ header, grades }: CourseOverviewProps) => {
const isCourse = typeof header !== 'string';
return (
<div className="flex flex-col gap-2">
<p className="text-2xl font-bold text-center">
{searchQueryLabel(course)}
{isCourse ? searchQueryLabel(header) : header}
</p>
{grades.state === 'done' && (
<p className="text-lg font-semibold text-center">
{'Overall grade: ' + grades.data.letter_grade}
</p>
{isCourse && (
<>
{typeof grades !== 'undefined' && grades.state === 'done' && (
<p className="text-lg font-semibold text-center">
{'Overall grade: ' + grades.data.letter_grade}
</p>
)}
<a
href={
TRENDS_URL +
'dashboard?searchTerms=' +
encodeURIComponent(searchQueryLabel(header))
}
target="_blank"
className="underline text-blue-600 hover:text-blue-800 visited:text-purple-600 text-center"
rel="noreferrer"
>
See on Trends
</a>
</>
)}
<a
href={
TRENDS_URL +
'dashboard?searchTerms=' +
encodeURIComponent(searchQueryLabel(course))
}
target="_blank"
className="underline text-blue-600 hover:text-blue-800 visited:text-purple-600 text-center"
rel="noreferrer"
>
See on Trends
</a>
</div>
);
};
Expand Down
25 changes: 22 additions & 3 deletions src/components/SearchResultsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,19 @@ type RowProps = {
backupGrades: GenericFetchedData<GradesType>;
rmp: GenericFetchedData<RMPInterface>;
setPage: (arg0: SearchQuery) => void;
showProfNameOnly: boolean;
fallbackToProfOnly: boolean;
};

function Row({ course, grades, backupGrades, rmp, setPage }: RowProps) {
function Row({
course,
grades,
backupGrades,
rmp,
setPage,
showProfNameOnly,
fallbackToProfOnly,
}: RowProps) {
const [open, setOpen] = useState(false);
const canOpen =
!(typeof grades === 'undefined' || grades.state === 'error') ||
Expand Down Expand Up @@ -78,7 +88,9 @@ function Row({ course, grades, backupGrades, rmp, setPage }: RowProps) {
}
className="leading-tight text-lg text-gray-600 dark:text-gray-200"
>
{searchQueryLabel(convertToProfOnly(course))}
{searchQueryLabel(
showProfNameOnly ? convertToProfOnly(course) : course,
)}
</Typography>
</TableCell>
</TableRow>
Expand Down Expand Up @@ -118,7 +130,8 @@ function Row({ course, grades, backupGrades, rmp, setPage }: RowProps) {
</Tooltip>
</TableCell>
<TableCell align="right" className="border-b-0">
{((typeof grades === 'undefined' || grades.state === 'error') &&
{(fallbackToProfOnly &&
(typeof grades === 'undefined' || grades.state === 'error') &&
(((typeof backupGrades === 'undefined' ||
backupGrades.state === 'error') && <></>) ||
(backupGrades.state === 'loading' && (
Expand Down Expand Up @@ -232,13 +245,17 @@ type SearchResultsTableProps = {
grades: { [key: string]: GenericFetchedData<GradesType> };
rmp: { [key: string]: GenericFetchedData<RMPInterface> };
setPage: (arg0: SearchQuery) => void;
showProfNameOnly: boolean;
fallbackToProfOnly: boolean;
};

const SearchResultsTable = ({
results,
grades,
rmp,
setPage,
showProfNameOnly,
fallbackToProfOnly,
}: SearchResultsTableProps) => {
//Table sorting category
const [orderBy, setOrderBy] = useState<'none' | 'gpa' | 'rating'>('none');
Expand Down Expand Up @@ -374,6 +391,8 @@ const SearchResultsTable = ({
backupGrades={grades[searchQueryLabel(convertToProfOnly(result))]}
rmp={rmp[searchQueryLabel(convertToProfOnly(result))]}
setPage={setPage}
showProfNameOnly={showProfNameOnly}
fallbackToProfOnly={fallbackToProfOnly}
/>
))}
</TableBody>
Expand Down
43 changes: 28 additions & 15 deletions src/content.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import type { PlasmoCSConfig } from 'plasmo';

export interface CourseHeader {
subjectPrefix: string;
courseNumber: string;
}
import { type SearchQuery, searchQueryEqual } from '~utils/SearchQuery';

// Plasmo CS config export
export const config: PlasmoCSConfig = {
Expand All @@ -22,7 +18,7 @@ export const config: PlasmoCSConfig = {
*/
export async function scrapeCourseData() {
const [header, professors] = await Promise.all([
getCourseInfo(),
getHeader(),
injectAndGetProfessorNames(),
]);
return { header: header, professors: professors };
Expand All @@ -46,16 +42,21 @@ export async function scrapeCourseData() {
});
}

/** Gets the course prefix and number from the course page */
async function getCourseInfo(): Promise<CourseHeader> {
const course = await waitForElement('h1');
const courseData = course.innerText.split(' ');
return { subjectPrefix: courseData[0], courseNumber: courseData[1] };
/** Gets the header or course prefix and number from the page */
async function getHeader(): Promise<string | SearchQuery> {
const header = (await waitForElement('h1')).innerText.trim();
if (header.match(/^[a-zA-Z]{2,4} [0-9][0-9V]?[0-9]{0,2}$/)) {
// is course
const courseData = header.split(' ');
return { prefix: courseData[0], number: courseData[1] };
}
// is text
return header;
}

/** Gets all professor names and then injects them into the section table */
async function injectAndGetProfessorNames(): Promise<string[]> {
const professors: string[] = [];
async function injectAndGetProfessorNames(): Promise<SearchQuery[]> {
const professors: SearchQuery[] = [];
const courseTable = await waitForElement('table');
const courseRows = courseTable.querySelectorAll('tbody');

Expand Down Expand Up @@ -95,10 +96,17 @@ export async function scrapeCourseData() {
sectionDetailsButton.click();
const sectionDetails = courseRow.querySelector('tr:nth-child(2)');
const sectionDetailsList = sectionDetails.querySelectorAll('li');
let searchQuery: SearchQuery = {};
let professor;
sectionDetailsList.forEach((li) => {
const detailLabelText =
li.querySelector<HTMLElement>('strong > span').innerText;
if (detailLabelText.includes('Subject')) {
searchQuery.prefix = li.innerText.split(':')[1].trim();
}
if (detailLabelText.includes('Course')) {
searchQuery.number = li.innerText.split(':')[1].trim();
}
if (detailLabelText.includes('Instructor')) {
professor = li.innerText.split(':')[1].trim();
}
Expand All @@ -108,9 +116,14 @@ export async function scrapeCourseData() {
newTd.innerText = professor ?? 'No Instructor';
if (typeof professor !== 'undefined') {
// this is in case we have multiple instructions per section
const sectionProfessors = professor.split(',');
const sectionProfessors = professor.trim().split(',');
sectionProfessors.forEach((sectionProfessor) => {
professors.push(sectionProfessor.trim());
const splitProf = sectionProfessor.trim().split(' ');
professors.push({
...searchQuery,
profFirst: splitProf[0],
profLast: splitProf[splitProf.length - 1],
});
});
}
const courseRowCells = courseRow.querySelector('tr');
Expand Down
38 changes: 15 additions & 23 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ const Index = () => {
}
setPage(set);
}
const [course, setCourse] = useState<SearchQuery>({});
const [header, setHeader] = useState<string | SearchQuery>('');

const [results, setResults] = useState<SearchQuery[]>([]);

Expand All @@ -198,27 +198,13 @@ const Index = () => {
return;
}
setPage('list');
const newCourse = {
prefix: payload.header.subjectPrefix,
number: payload.header.courseNumber,
};
setCourse(newCourse);
fetchAndStoreGradesData(newCourse);
let newResults = [];
for (const professor of payload.professors) {
const splitProf = professor.split(' ');
const profFirst = splitProf[0];
const profLast = splitProf[splitProf.length - 1];
newResults.push({
prefix: payload.header.subjectPrefix,
number: payload.header.courseNumber,
profFirst: profFirst,
profLast: profLast,
});
setHeader(payload.header);
console.log(payload.header, typeof payload.header);
if (typeof payload.header !== 'string') {
fetchAndStoreGradesData(payload.header);
}
newResults = removeDuplicates(newResults);
setResults(newResults);
getData(newResults);
setResults(removeDuplicates(payload.professors));
getData(payload.professors);
});
}, []);

Expand Down Expand Up @@ -318,15 +304,21 @@ const Index = () => {
>
<div className="p-4">
<CourseOverview
course={course}
grades={grades[searchQueryLabel(course)]}
header={header}
grades={
typeof header !== 'string'
? grades[searchQueryLabel(header)]
: undefined
}
/>
</div>
<SearchResultsTable
results={results}
grades={grades}
rmp={rmp}
setPage={setPageAndScroll}
showProfNameOnly={typeof header !== 'string'}
fallbackToProfOnly={typeof header !== 'string'}
/>
</div>
{page !== 'list' && (
Expand Down

0 comments on commit 2d62f0e

Please sign in to comment.