Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35a85d8e83 | |||
| 71b0a6e4dd | |||
| d4fbd04466 | |||
| 31b3814b1e | |||
| 1aff2b0a68 | |||
| 2567fcadbd | |||
| d9e0e9f84e | |||
| 59b31ea43f | |||
| 2c8b3e0f84 | |||
| 6910a7b5ea | |||
| cbdcfc4ca1 | |||
| feabc336c1 | |||
| b5b9f14a49 | |||
| fc93cd1248 | |||
| 6e58c634a1 | |||
| 10cca344c7 | |||
| cdf58ea3a0 | |||
| d97c80afbb | |||
| 8c7d028f5b |
@@ -22,7 +22,7 @@ This is a website that generates visual representation of students' grade data o
|
|||||||
|
|
||||||
**Here's how it looks like right now:** *(Now all of you know my grades ;-;)*
|
**Here's how it looks like right now:** *(Now all of you know my grades ;-;)*
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import JsonUtils from '@/utils/json-utils';
|
|||||||
import pWaitFor from 'p-wait-for';
|
import pWaitFor from 'p-wait-for';
|
||||||
import {HttpUtils} from '@/utils/http-utils';
|
import {HttpUtils} from '@/utils/http-utils';
|
||||||
import {CourseUtils} from '@/utils/course-utils';
|
import {CourseUtils} from '@/utils/course-utils';
|
||||||
|
import {GPAUtils} from '@/utils/gpa-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Objects of this interface represent assignment grades.
|
* Objects of this interface represent assignment grades.
|
||||||
@@ -41,6 +42,12 @@ export interface Course
|
|||||||
level: string,
|
level: string,
|
||||||
scaleUp: number,
|
scaleUp: number,
|
||||||
|
|
||||||
|
grading:
|
||||||
|
{
|
||||||
|
method: string,
|
||||||
|
weightingMap: {[index: string]: number}
|
||||||
|
}
|
||||||
|
|
||||||
assignments: Grade[]
|
assignments: Grade[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,12 +161,11 @@ export default class App extends Vue
|
|||||||
// Wait for assignments to be ready.
|
// Wait for assignments to be ready.
|
||||||
pWaitFor(() => this.isAssignmentsReady()).then(() =>
|
pWaitFor(() => this.isAssignmentsReady()).then(() =>
|
||||||
{
|
{
|
||||||
// When the assignments are ready
|
|
||||||
// TODO: Display loading
|
|
||||||
this.assignmentsReady = true;
|
|
||||||
|
|
||||||
// Filter courses
|
// Filter courses
|
||||||
this.filteredCourses = CourseUtils.getGradedCourses(this.courses);
|
this.filteredCourses = CourseUtils.getGradedCourses(this.courses);
|
||||||
|
|
||||||
|
// Check grading algorithms
|
||||||
|
this.checkGradingAlgorithms();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +184,67 @@ export default class App extends Vue
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the courses' grading algorithms. (Total-average or percent-type)
|
||||||
|
*/
|
||||||
|
private checkGradingAlgorithms()
|
||||||
|
{
|
||||||
|
// Loop through all the courses
|
||||||
|
for (const course of this.filteredCourses)
|
||||||
|
{
|
||||||
|
// Check if total-average grade is the same with percent-type grade
|
||||||
|
if (course.numericGrade == GPAUtils.getTotalMeanAverage(course))
|
||||||
|
{
|
||||||
|
course.grading = {method: 'TOTAL_AVERAGE', weightingMap: {}};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Request grading scheme for this course
|
||||||
|
this.http.post('/grading', {'assignment_id': course.assignmentsId}).then(response =>
|
||||||
|
{
|
||||||
|
// Check success
|
||||||
|
if (response.success)
|
||||||
|
{
|
||||||
|
// Add it to course
|
||||||
|
course.grading = response.data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Show error message TODO: Show it properly
|
||||||
|
alert(response.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(alert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for done
|
||||||
|
pWaitFor(() => this.isGradingReady()).then(() =>
|
||||||
|
{
|
||||||
|
// When the assignments are ready
|
||||||
|
// TODO: Display loading
|
||||||
|
this.assignmentsReady = true;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are grading algorithms ready or not.
|
||||||
|
*
|
||||||
|
* @returns boolean Ready or not
|
||||||
|
*/
|
||||||
|
private isGradingReady(): boolean
|
||||||
|
{
|
||||||
|
for (const course of this.filteredCourses)
|
||||||
|
{
|
||||||
|
if (course.grading == undefined)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when a navigation tab is clicked
|
* This is called when a navigation tab is clicked
|
||||||
*
|
*
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ export default class Constants
|
|||||||
'| |. , |---|,---.,---.| , .,---,,---.,---.\n' +
|
'| |. , |---|,---.,---.| , .,---,,---.,---.\n' +
|
||||||
' \\ / >< | || |,---|| | | .-\' |---\'| \n' +
|
' \\ / >< | || |,---|| | | .-\' |---\'| \n' +
|
||||||
' `\' \' ` ` \'` \'`---^`---\'`---|\'---\'`---\'` \n' +
|
' `\' \' ` ` \'` \'`---^`---\'`---|\'---\'`---\'` \n' +
|
||||||
' v0.2.1.295 `---\' ';
|
' v0.2.3.315 `---\' ';
|
||||||
|
|
||||||
// Graph Theme
|
// Graph Theme
|
||||||
public static THEME =
|
public static THEME =
|
||||||
|
|||||||
@@ -83,17 +83,6 @@ export default class GraphOverall extends Vue
|
|||||||
dates.push(new Date(date));
|
dates.push(new Date(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize course specific variables
|
|
||||||
let courseScores: {[index: string]: any} = {};
|
|
||||||
let courseMaxScores: {[index: string]: any} = {};
|
|
||||||
let courseIndexes: {[index: string]: any} = {};
|
|
||||||
courses.forEach(course =>
|
|
||||||
{
|
|
||||||
courseScores[course.name] = 0;
|
|
||||||
courseMaxScores[course.name] = 0;
|
|
||||||
courseIndexes[course.name] = course.assignments.length - 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Compute the rows data
|
// Compute the rows data
|
||||||
let rows: {[index: string]: any}[] = [];
|
let rows: {[index: string]: any}[] = [];
|
||||||
dates.forEach(date =>
|
dates.forEach(date =>
|
||||||
@@ -104,33 +93,78 @@ export default class GraphOverall extends Vue
|
|||||||
// Loop through courses
|
// Loop through courses
|
||||||
courses.forEach(course =>
|
courses.forEach(course =>
|
||||||
{
|
{
|
||||||
// Reversed loop through the assignments
|
// Total Mean
|
||||||
for (let r = courseIndexes[course.name]; r >= 0; r--)
|
if (course.grading.method == 'TOTAL_MEAN')
|
||||||
{
|
{
|
||||||
let assignment = course.assignments[r];
|
let score = 0;
|
||||||
|
let max = 0;
|
||||||
|
|
||||||
// If assignment should be displayed
|
// Loop through assignments
|
||||||
if (assignment.complete != 'Complete') continue;
|
course.assignments.forEach(assignment =>
|
||||||
|
|
||||||
// Date is being looked at
|
|
||||||
let assignmentDate = new Date(assignment.date);
|
|
||||||
if (assignmentDate.getTime() == date.getTime())
|
|
||||||
{
|
{
|
||||||
// Record scores
|
// If assignment should be displayed
|
||||||
courseScores[course.name] += assignment.score;
|
if (assignment.complete != 'Complete') return;
|
||||||
courseMaxScores[course.name] += assignment.scoreMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not now
|
// Date is being looked at
|
||||||
else if (assignmentDate > date)
|
let assignmentDate = new Date(assignment.date);
|
||||||
{
|
if (assignmentDate.getTime() < date.getTime())
|
||||||
courseIndexes[course.name] = r;
|
{
|
||||||
break;
|
// Record scores
|
||||||
}
|
score += assignment.score;
|
||||||
|
max += assignment.scoreMax;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add average to the row
|
||||||
|
row[course.name] = score / max * 100;
|
||||||
}
|
}
|
||||||
|
else if (course.grading.method == 'PERCENT_TYPE')
|
||||||
|
{
|
||||||
|
let typeScores: {[index: string]: any} = {};
|
||||||
|
let typeCounts: {[index: string]: any} = {};
|
||||||
|
|
||||||
// Add average to the row
|
// Loop through assignments
|
||||||
row[course.name] = courseScores[course.name] / courseMaxScores[course.name] * 100;
|
course.assignments.forEach(assignment =>
|
||||||
|
{
|
||||||
|
// If assignment should be displayed
|
||||||
|
if (assignment.complete != 'Complete') return;
|
||||||
|
|
||||||
|
// Date is being looked at
|
||||||
|
let assignmentDate = new Date(assignment.date);
|
||||||
|
if (assignmentDate.getTime() < date.getTime())
|
||||||
|
{
|
||||||
|
// Record scores
|
||||||
|
if (typeScores[assignment.type] == undefined) typeScores[assignment.type] = 0;
|
||||||
|
typeScores[assignment.type] += assignment.score / assignment.scoreMax;
|
||||||
|
|
||||||
|
if (typeCounts[assignment.type] == undefined) typeCounts[assignment.type] = 0;
|
||||||
|
typeCounts[assignment.type] ++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Count total percentage (This is to avoid less than expected cases)
|
||||||
|
// Eg. If HW = 25% and Quiz = 75%, I have 1 hw and 0 quiz
|
||||||
|
// Without total percentage, the avg grade I get is 25%.
|
||||||
|
let totalPercentage = 0;
|
||||||
|
for (let type in course.grading.weightingMap)
|
||||||
|
{
|
||||||
|
if (typeScores[type] != undefined)
|
||||||
|
{
|
||||||
|
totalPercentage += course.grading.weightingMap[type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count
|
||||||
|
let score = 0;
|
||||||
|
for (let type in typeScores)
|
||||||
|
{
|
||||||
|
let typeFactor = course.grading.weightingMap[type] / totalPercentage;
|
||||||
|
score += typeScores[type] * typeFactor / typeCounts[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add average to the row
|
||||||
|
if (score != 0) row[course.name] = score * 100;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add it to the array
|
// Add it to the array
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ export class CourseUtils
|
|||||||
// Skip courses without assignments
|
// Skip courses without assignments
|
||||||
if (course.assignments.length == 0) return;
|
if (course.assignments.length == 0) return;
|
||||||
|
|
||||||
|
// Skip if there are no grading scale
|
||||||
|
// if (course.grading.method == 'NOT_GRADED') return;
|
||||||
|
|
||||||
// Add it to the list
|
// Add it to the list
|
||||||
result.push(course);
|
result.push(course);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// Initialize course specific variables
|
||||||
|
let courseScores: {[index: string]: any} = {};
|
||||||
|
let courseMaxScores: {[index: string]: any} = {};
|
||||||
|
let courseIndexes: {[index: string]: any} = {};
|
||||||
|
courses.forEach(course =>
|
||||||
|
{
|
||||||
|
courseScores[course.name] = 0;
|
||||||
|
courseMaxScores[course.name] = 0;
|
||||||
|
courseIndexes[course.name] = course.assignments.length - 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Compute the rows data
|
||||||
|
let rows: {[index: string]: any}[] = [];
|
||||||
|
dates.forEach(date =>
|
||||||
|
{
|
||||||
|
// Define row object
|
||||||
|
let row: {[index: string]:any} = {'date': date.toLocaleDateString('en-US')};
|
||||||
|
|
||||||
|
// Loop through courses
|
||||||
|
courses.forEach(course =>
|
||||||
|
{
|
||||||
|
// Reversed loop through the assignments
|
||||||
|
for (let r = courseIndexes[course.name]; r >= 0; r--)
|
||||||
|
{
|
||||||
|
let assignment = course.assignments[r];
|
||||||
|
|
||||||
|
// If assignment should be displayed
|
||||||
|
if (assignment.complete != 'Complete') continue;
|
||||||
|
|
||||||
|
// Date is being looked at
|
||||||
|
let assignmentDate = new Date(assignment.date);
|
||||||
|
if (assignmentDate.getTime() == date.getTime())
|
||||||
|
{
|
||||||
|
// Detect grading method and record scores
|
||||||
|
if (course.grading.method == 'TOTAL_MEAN')
|
||||||
|
{
|
||||||
|
courseScores[course.name] += assignment.score;
|
||||||
|
courseMaxScores[course.name] += assignment.scoreMax;
|
||||||
|
}
|
||||||
|
else if (course.grading.method == 'PERCENT_TYPE')
|
||||||
|
{
|
||||||
|
let scale = course.grading.weightingMap[assignment.type];
|
||||||
|
courseScores[course.name] += assignment.score * scale;
|
||||||
|
courseMaxScores[course.name] += assignment.scoreMax * scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not now
|
||||||
|
else if (assignmentDate > date)
|
||||||
|
{
|
||||||
|
courseIndexes[course.name] = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add average to the row
|
||||||
|
row[course.name] = courseScores[course.name] / courseMaxScores[course.name] * 100;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add it to the array
|
||||||
|
rows.push(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
else if (course.grading.method == 'PERCENT_TYPE')
|
||||||
|
{
|
||||||
|
let typeScores: {[index: string]: any} = {};
|
||||||
|
let typeCounts: {[index: string]: any} = {};
|
||||||
|
|
||||||
|
// Loop through assignments
|
||||||
|
course.assignments.forEach(assignment =>
|
||||||
|
{
|
||||||
|
// If assignment should be displayed
|
||||||
|
if (assignment.complete != 'Complete') return;
|
||||||
|
|
||||||
|
// Date is being looked at
|
||||||
|
let assignmentDate = new Date(assignment.date);
|
||||||
|
if (assignmentDate.getTime() < date.getTime())
|
||||||
|
{
|
||||||
|
// Record scores
|
||||||
|
if (typeScores[assignment.type] == undefined) typeScores[assignment.type] = 0;
|
||||||
|
typeScores[assignment.type] += assignment.score / assignment.scoreMax;
|
||||||
|
|
||||||
|
if (typeCounts[assignment.type] == undefined) typeCounts[assignment.type] = 0;
|
||||||
|
typeCounts[assignment.type] ++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
// Count
|
||||||
|
for (let type in typeScores)
|
||||||
|
{
|
||||||
|
score += typeScores[type] * course.grading.weightingMap[type] / typeCounts[type];
|
||||||
|
console.log(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add average to the row
|
||||||
|
row[course.name] = score * 100;
|
||||||
|
}
|
||||||
@@ -102,4 +102,29 @@ export class GPAUtils
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the total-mean (total/max) average
|
||||||
|
*
|
||||||
|
* @param course Course
|
||||||
|
*/
|
||||||
|
public static getTotalMeanAverage(course: Course)
|
||||||
|
{
|
||||||
|
let score = 0;
|
||||||
|
let max = 0;
|
||||||
|
|
||||||
|
// Loop through assignments
|
||||||
|
course.assignments.forEach(assignment =>
|
||||||
|
{
|
||||||
|
// If assignment should be displayed
|
||||||
|
if (assignment.complete != 'Complete') return;
|
||||||
|
|
||||||
|
// Record scores
|
||||||
|
score += assignment.score;
|
||||||
|
max += assignment.scoreMax;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return
|
||||||
|
return score / max * 100;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user