Skip to content

Commit 5362b20

Browse files
committed
add gpa calculation
Signed-off-by: Anvay Mathur <[email protected]>
1 parent 50d5751 commit 5362b20

File tree

5 files changed

+256
-4
lines changed

5 files changed

+256
-4
lines changed

CONTRIBUTING.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,37 @@ The main branch for version 2 is v2. Do not force push into it; please create pu
33

44
This is a work in progress, so documentation files such as this and README.md are not fully fleshed out. Any help in this would be appreciated.
55

6-
If you want to make a new feature, please create an issue for it and link it in your pull request.
6+
If you want to make a new feature, please create an issue for it and link it in your pull request.
7+
8+
### License Header
9+
10+
If you modify an existing file, add your copyright notice to the file. Something like this:
11+
```
12+
@copyright Copyright (c) <year> <your name> <<your email address>>
13+
```
14+
For a new file, add this license header to the top of the file:
15+
```
16+
/**
17+
*
18+
* @copyright Copyright (c) <year> <your name> <<your email address>>
19+
*
20+
* @author <your name> <<your email address>>
21+
*
22+
* @license GNU AGPL version 3 only
23+
*
24+
* SAS Powerschool Enhancement Suite - A browser extension to improve the experience of SAS Powerschool.
25+
*
26+
* This program is free software: you can redistribute it and/or modify
27+
* it under the terms of the GNU Affero General Public License as
28+
* published by the Free Software Foundation, version 3.
29+
*
30+
* This program is distributed in the hope that it will be useful,
31+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
32+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33+
* GNU Affero General Public License for more details.
34+
*
35+
* You should have received a copy of the GNU Affero General Public License
36+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
37+
*
38+
*/
39+
```
Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,94 @@
11
<script lang="ts">
2+
import { gradeToPercent, listOfGrades } from "../..//models/grades";
3+
import type { ClassManager } from "../../models/classes";
4+
5+
export let classManager: ClassManager;
6+
7+
console.log("RERENDERING");
8+
9+
$: sem1GPA = classManager.calculateGPA(1);
10+
$: sem2GPA = classManager.calculateGPA(2);
11+
12+
let editGrades = false;
213
</script>
314

4-
<p>GPA</p>
15+
{#if sem1GPA && sem1GPA !== -1}
16+
<p>First Semester GPA: {classManager.calculateGPA(1).toFixed(2)}</p>
17+
{:else}
18+
<p>First Semester GPA: N/A</p>
19+
{/if}
20+
21+
{#if sem2GPA && sem2GPA !== -1}
22+
<p>Second Semester GPA: {classManager.calculateGPA(2).toFixed(2)}</p>
23+
{:else}
24+
<p>Second Semester GPA: N/A</p>
25+
{/if}
26+
27+
<label class="tw-flex tw-items-center tw-gap-2">
28+
<input type="checkbox" class="!tw-m-0" bind:checked={editGrades} />
29+
Edit grades for GPA calculation
30+
</label>
31+
32+
{#if editGrades}
33+
<table class="!tw-w-auto grid zebra tw-mb-4">
34+
<thead>
35+
<th> Class </th>
36+
<th> S1 Grade </th>
37+
<th> S2 Grade </th>
38+
<th> Credits </th>
39+
<th> AP/AT </th>
40+
</thead>
41+
<tbody>
42+
{#each classManager.classes as c, i}
43+
<tr>
44+
<td> {c.name} </td>
45+
<td>
46+
<select
47+
class="tw-rounded-md tw-h-full tw-border-[#CCCCCC] tw-border-solid tw-border tw-p-1"
48+
bind:value={classManager.classes[i].grade.s1}
49+
>
50+
{#each listOfGrades as grade}
51+
<option value={grade}
52+
>{grade}
53+
{grade !== "INC" ? `(${gradeToPercent[grade]}%)` : ""}
54+
</option>
55+
{/each}
56+
</select>
57+
</td>
58+
<td>
59+
<select
60+
class="tw-rounded-md tw-h-full tw-border-[#CCCCCC] tw-border-solid tw-border tw-p-1"
61+
bind:value={classManager.classes[i].grade.s2}
62+
>
63+
{#each listOfGrades as grade}
64+
<option value={grade}
65+
>{grade}
66+
{grade !== "INC" ? `(${gradeToPercent[grade]}%)` : ""}
67+
</option>
68+
{/each}
69+
<option value={null}>No grade</option>
70+
</select>
71+
</td>
72+
<td>
73+
<select
74+
class="tw-rounded-md tw-h-full tw-border-[#CCCCCC] tw-border-solid tw-border tw-p-1"
75+
bind:value={classManager.classes[i].credits}
76+
>
77+
<option value={0}>0</option>
78+
<option value={0.5}>0.5</option>
79+
<option value={1}>1</option>
80+
<option value={2}>2</option>
81+
</select>
82+
</td>
83+
<td class="tw-align-middle tw-text-center">
84+
<input
85+
type="checkbox"
86+
class="!tw-m-0"
87+
bind:checked={classManager.classes[i].isBoosted}
88+
/>
89+
</td>
90+
</tr>
91+
{/each}
92+
</tbody>
93+
</table>
94+
{/if}

src/content_script/guardianHome/index.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,41 @@
2222
*
2323
*/
2424

25+
import { Class, ClassManager } from "../../models/classes";
2526
import GPA from "./GPA.svelte";
27+
import { listOfGrades, type Grade } from "../../models/grades";
2628

27-
console.log("WASSUP")
29+
const classManager = new ClassManager([]);
30+
31+
const rows = document.querySelectorAll(".linkDescList.grid > tbody > tr.center:not(.th2)");
32+
33+
for (const row of rows) {
34+
const nameEle = row.querySelector("td:nth-child(2)");
35+
const s1GradeEle = row.querySelector("td:nth-child(3)");
36+
const s2GradeEle = row.querySelector("td:nth-child(4)");
37+
38+
if (!nameEle || !s1GradeEle || !s2GradeEle) continue;
39+
40+
const name = nameEle.firstChild?.textContent?.trim();
41+
if (!name) continue;
42+
43+
let s1Grade: string | null = s1GradeEle.textContent?.trim()!;
44+
if (!s1Grade) continue;
45+
if (!listOfGrades.includes(s1Grade as Grade)) s1Grade = null;
46+
47+
let s2Grade: string | null = s2GradeEle.textContent?.trim()!;
48+
if (!s2Grade) continue;
49+
if (!listOfGrades.includes(s2Grade as Grade)) s2Grade = null;
50+
51+
if (!s1Grade && !s2Grade) continue;
52+
classManager.addClass(new Class(name, { s1: s1Grade as Grade | null, s2: s2Grade as Grade | null }))
53+
}
54+
55+
56+
console.log(classManager);
2857
const target = document.createElement("div");
2958
document
3059
.querySelector("#content-main > .box-round")
3160
?.insertBefore(target, document.querySelector("#quickLookup"));
3261

33-
new GPA({ target: target as Element });
62+
new GPA({ target: target as Element, props: { classManager: classManager } });

src/content_script/home/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424

2525
import Home from "./Home.svelte";
2626

27+
28+
29+
2730
console.log("rendering");
2831
const target = document.createElement("div");
2932
document

src/models/classes.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
*
3+
* @copyright Copyright (c) 2023-2024 Anvay Mathur <[email protected]>
4+
*
5+
* @author Anvay Mathur <[email protected]>
6+
*
7+
* @license GNU AGPL-3.0-only
8+
*
9+
* SAS Powerschool Enhancement Suite - A browser extension to improve the experience of SAS Powerschool.
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, version 3.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License
21+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*
23+
*/
24+
25+
import type { Grade } from "./grades";
26+
27+
export const gradeToGPA = {
28+
"A+": 4.5,
29+
A: 4,
30+
"B+": 3.5,
31+
B: 3,
32+
"C+": 2.5,
33+
C: 2,
34+
"D+": 1.5,
35+
D: 1,
36+
F: 0,
37+
INC: 0,
38+
} as const;
39+
40+
export class ClassManager {
41+
public classes: Class[];
42+
43+
constructor(classes: Class[]) {
44+
this.classes = classes;
45+
}
46+
47+
public getTotalCredits(semester: 1 | 2): number {
48+
return this.classes.reduce((acc, cur) => {
49+
if (cur.grade[`s${semester}`] !== null && cur.grade[`s${semester}`] !== "INC") return acc + cur.credits;
50+
return acc;
51+
}, 0);
52+
}
53+
54+
public calculateGPA(semester: 1 | 2): number {
55+
let gpa = 0;
56+
let totalCredits = this.getTotalCredits(semester);
57+
if (totalCredits === 0) return -1;
58+
for (const c of this.classes) {
59+
if (c.grade[`s${semester}`] === "INC") {
60+
continue;
61+
}
62+
if (c.grade[`s${semester}`] !== null) gpa += gradeToGPA[c.grade[`s${semester}`]!] * c.credits + (c.isBoosted ? 0.5 : 0);
63+
}
64+
return gpa / totalCredits;
65+
}
66+
67+
public addClass(c: Class) {
68+
this.classes.push(c);
69+
}
70+
}
71+
72+
export class Class {
73+
public id: number;
74+
public name: string;
75+
public static nextId: number = 1;
76+
public credits: number;
77+
public grade: {
78+
s1: Grade | null,
79+
s2: Grade | null
80+
};
81+
public isBoosted: boolean;
82+
83+
constructor(name: string, grade: { s1: Grade | null, s2: Grade | null }) {
84+
this.id = Class.nextId++;
85+
this.name = name;
86+
this.grade = grade;
87+
this.isBoosted = name.includes("AP ") || name.includes("AT ");
88+
89+
if (name.includes("English 10/American History") || name.includes("English 9/World History")) {
90+
this.credits = 2;
91+
} else if (/^(I Service: |IS: )/.test(name)) {
92+
this.credits = 0.5;
93+
} else {
94+
this.credits = 1;
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)