Compare commits
93 Commits
0.4.2.912
...
0.4.3.1006
| Author | SHA1 | Date | |
|---|---|---|---|
| 6204efd453 | |||
| db083732b0 | |||
| 4974049c0b | |||
| 857192ee6d | |||
| 90d07b1faa | |||
| 4abe02da94 | |||
| adca4b41e2 | |||
| 4d9d8e0be5 | |||
| 546ad81f7c | |||
| 1eff27ad26 | |||
| f540e03a56 | |||
| af925741b4 | |||
| 08bb24cac4 | |||
| 8a9ca83e68 | |||
| e6b6a73f1f | |||
| ceca351b07 | |||
| 4e14730db6 | |||
| 3b0e291df4 | |||
| 6bcb2577f7 | |||
| 54e54b89e9 | |||
| 0085d384fd | |||
| 3b4e40261f | |||
| eb8715867e | |||
| 1bcfdf5648 | |||
| 429de6553b | |||
| fce47648c8 | |||
| f6d44dd1f2 | |||
| 4fe0a54277 | |||
| e8bf21e60c | |||
| 472df39ac8 | |||
| ae94f54a7b | |||
| 8c9f0a0e83 | |||
| cb22baf120 | |||
| 0525aae98a | |||
| 4c87cb2947 | |||
| 076a1ee52e | |||
| 7bd9e97396 | |||
| aa014bcdab | |||
| ee308d29ff | |||
| 35ef2144b7 | |||
| 6dc2a21e8f | |||
| 343c921eb2 | |||
| f54dfba058 | |||
| a7d7ef44a6 | |||
| 793d7444b2 | |||
| eaf4d2ce7b | |||
| 79e615ee46 | |||
| 40353cfd35 | |||
| 2be2ce98e6 | |||
| 6659f65763 | |||
| 5b3ba4db07 | |||
| a05281f4e4 | |||
| 2f95548fb3 | |||
| 96ee9e9265 | |||
| 9026b9d3a9 | |||
| 67e38dd554 | |||
| 7ffef67e42 | |||
| 0d195cfb7f | |||
| 0d3e9c0840 | |||
| 6c600f31f8 | |||
| a05a44aaef | |||
| 31d3a5a09e | |||
| 30293cd261 | |||
| 9a279b3417 | |||
| 4a7ff0ea55 | |||
| fd341e9d33 | |||
| bdacc8dd9e | |||
| ad2c8a1ee8 | |||
| 1f58818a1a | |||
| 09442cbfba | |||
| 91e10d1fa8 | |||
| 80267feb54 | |||
| f4ee2dadb6 | |||
| d698f3d13a | |||
| 82cb845061 | |||
| 940738307b | |||
| df011787e1 | |||
| 0af393a1e4 | |||
| 6504cc033b | |||
| c6ccc5e311 | |||
| 3a8899507f | |||
| 2b7026c4ce | |||
| a3fd822252 | |||
| f9d7fa398f | |||
| 7ffc445bba | |||
| 8e9f6a4bb7 | |||
| 05cb560c8c | |||
| 53a57234a0 | |||
| 6af6bb0959 | |||
| 089aad7398 | |||
| 937e89ce5f | |||
| b0685ffd6b | |||
| 18dee50b96 |
+2
-1
@@ -3,7 +3,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<!--meta name="viewport" content="width=device-width,initial-scale=1.0"-->
|
||||
<meta name="viewport" content="width=1024">
|
||||
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>Veracross Analyzer</title>
|
||||
|
||||
@@ -4,36 +4,13 @@ import Navigation from '@/components/navigation/navigation';
|
||||
import Overall from '@/pages/overall/overall.vue';
|
||||
import Constants from '@/constants';
|
||||
import pWaitFor from 'p-wait-for';
|
||||
import {HttpUtils} from '@/utils/http-utils';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import {HttpUtils} from '@/logic/utils/http-utils';
|
||||
import {GPAUtils} from '@/logic/utils/gpa-utils';
|
||||
import Loading from '@/components/loading/loading.vue';
|
||||
import CoursePage from '@/pages/course/course-page.vue';
|
||||
import Course from '@/logic/course';
|
||||
|
||||
|
||||
/**
|
||||
* Objects of this interface represent assignment grades.
|
||||
*/
|
||||
export interface Assignment
|
||||
{
|
||||
id: number
|
||||
scoreId: number
|
||||
type: string
|
||||
typeId: number
|
||||
description: string
|
||||
date: Date
|
||||
complete: string
|
||||
include: boolean
|
||||
display: boolean
|
||||
|
||||
unread: boolean
|
||||
|
||||
scoreMax: number
|
||||
score: number
|
||||
|
||||
gradingPeriod: number
|
||||
}
|
||||
|
||||
@Component({
|
||||
components: {Login, Navigation, Overall, Loading, CoursePage},
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Component, Vue} from 'vue-property-decorator';
|
||||
import Constants from '@/constants';
|
||||
import App from '@/components/app/app';
|
||||
import VersionUtils from '@/utils/version-utils';
|
||||
import VersionUtils from '@/logic/utils/version-utils';
|
||||
|
||||
/**
|
||||
* This component handles user login, and obtains data from the server.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import App from '@/components/app/app';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
import {CourseUtils} from '@/logic/utils/course-utils';
|
||||
import {FormatUtils} from '@/logic/utils/format-utils';
|
||||
import pWaitFor from 'p-wait-for';
|
||||
import Course from '@/logic/course';
|
||||
|
||||
|
||||
+9
-2
@@ -7,7 +7,7 @@ export default class Constants
|
||||
public static API_URL: string = 'https://va.hydev.org/api';
|
||||
|
||||
/** Current version */
|
||||
public static VERSION: string = '0.4.2.912';
|
||||
public static VERSION: string = '0.4.3.1006';
|
||||
|
||||
/** Minimum version that still supports the same cookies */
|
||||
public static MIN_SUPPORTED_VERSION: string = '0.3.4.561';
|
||||
@@ -56,6 +56,13 @@ export default class Constants
|
||||
};
|
||||
|
||||
// Terms (TODO: Actually get the terms dynamically
|
||||
public static TERMS = [new Date('Sep 04 2019'), new Date('Nov 01 2019'), new Date('Feb 02 2020')];
|
||||
public static TERMS =
|
||||
[
|
||||
new Date('Sep 04 2019'),
|
||||
new Date('Nov 03 2019'),
|
||||
new Date('Jan 19 2020'),
|
||||
new Date('Mar 22 2020'),
|
||||
new Date('Jun 05 2020'),
|
||||
];
|
||||
public static CURRENT_TERM = 1;
|
||||
}
|
||||
|
||||
+119
-34
@@ -1,9 +1,42 @@
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import JsonUtils from '@/utils/json-utils';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import JsonUtils from '@/logic/utils/json-utils';
|
||||
import {FormatUtils} from '@/logic/utils/format-utils';
|
||||
import {CourseUtils} from '@/logic/utils/course-utils';
|
||||
import Navigation from '@/components/navigation/navigation';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import {GPAUtils} from '@/logic/utils/gpa-utils';
|
||||
|
||||
/**
|
||||
* Objects of this interface represent assignment grades.
|
||||
*/
|
||||
export interface Assignment
|
||||
{
|
||||
id: number
|
||||
scoreId: number
|
||||
type: string
|
||||
typeId: number
|
||||
description: string
|
||||
time: number
|
||||
complete: string
|
||||
include: boolean
|
||||
display: boolean
|
||||
|
||||
unread: boolean
|
||||
|
||||
scoreMax: number
|
||||
score: number
|
||||
|
||||
gradingPeriod: number
|
||||
}
|
||||
|
||||
export interface AssignmentType
|
||||
{
|
||||
id: number
|
||||
name: string
|
||||
|
||||
weight: number
|
||||
scoreMax: number
|
||||
score: number
|
||||
percent: number
|
||||
}
|
||||
|
||||
export default class Course
|
||||
{
|
||||
@@ -79,7 +112,7 @@ export default class Course
|
||||
this.rawAssignments = JsonUtils.filterAssignments(data);
|
||||
|
||||
// Sort by date (Latest is at 0)
|
||||
this.rawAssignments.sort((a, b) => b.date.getTime() - a.date.getTime());
|
||||
this.rawAssignments.sort((a, b) => b.time - a.time);
|
||||
|
||||
// Filter assignments into terms
|
||||
let termAssignments: Assignment[][] = [[], [], [], []];
|
||||
@@ -104,7 +137,7 @@ export default class Course
|
||||
if (this.level == 'None' || this.level == 'Unknown' || this.scaleUp == -1) return false;
|
||||
|
||||
// Skip courses without graded assignments
|
||||
if (this.assignments.filter(a => a.complete == 'Complete').length == 0) return false;
|
||||
if (this.assignments.length == 0) return false;
|
||||
|
||||
// Skip if there are no grading scale
|
||||
// if (course.grading.method == 'NOT_GRADED') return;
|
||||
@@ -116,7 +149,7 @@ export default class Course
|
||||
/**
|
||||
* Get assignments of the selected time
|
||||
*/
|
||||
get assignments(): Assignment[]
|
||||
get rawSelectedAssignments(): Assignment[]
|
||||
{
|
||||
let timeCode = Navigation.instance.getSelectedGradingPeriod();
|
||||
|
||||
@@ -130,30 +163,42 @@ export default class Course
|
||||
return this.computed.termAssignments[timeCode];
|
||||
}
|
||||
|
||||
// TODO: Optimize this
|
||||
private letterGradeComputed = false;
|
||||
private _cacheAssignments: Assignment[];
|
||||
|
||||
/**
|
||||
* Get graded assignments
|
||||
*/
|
||||
get assignments(): Assignment[]
|
||||
{
|
||||
if (this._cacheAssignments == null)
|
||||
this._cacheAssignments = this.rawSelectedAssignments.filter(a => a.complete == 'Complete');
|
||||
|
||||
return this._cacheAssignments;
|
||||
}
|
||||
|
||||
private _cacheLetterGrade: string;
|
||||
|
||||
/**
|
||||
* Get letter grade
|
||||
*/
|
||||
get letterGrade(): string
|
||||
{
|
||||
// Cached
|
||||
if (this.rawLetterGrade != undefined && this.letterGradeComputed)
|
||||
return this.rawLetterGrade;
|
||||
this.letterGradeComputed = true;
|
||||
if (this._cacheLetterGrade == null)
|
||||
{
|
||||
// Get scale
|
||||
let scale = GPAUtils.findScale(this.numericGrade);
|
||||
|
||||
// Get scale
|
||||
let scale = GPAUtils.findScale(this.numericGrade);
|
||||
// Scale not found
|
||||
if (scale == undefined) return this._cacheLetterGrade = '--';
|
||||
|
||||
// Scale not found
|
||||
if (scale == undefined) return this.rawLetterGrade = '--';
|
||||
// Cache
|
||||
this._cacheLetterGrade = scale.letter;
|
||||
}
|
||||
|
||||
// Return
|
||||
return this.rawLetterGrade = scale.letter;
|
||||
return this._cacheLetterGrade;
|
||||
}
|
||||
|
||||
private numericGradeComputed = false;
|
||||
private _cacheNumericGrade: number;
|
||||
|
||||
/**
|
||||
* Get numeric grade
|
||||
@@ -161,21 +206,61 @@ export default class Course
|
||||
get numericGrade(): number
|
||||
{
|
||||
// Cached
|
||||
if (this.rawNumericGrade != undefined && this.numericGradeComputed)
|
||||
return this.rawNumericGrade;
|
||||
this.numericGradeComputed = true;
|
||||
|
||||
// Calculate
|
||||
if (this.grading.method == 'PERCENT_TYPE')
|
||||
if (this._cacheNumericGrade == null)
|
||||
{
|
||||
return this.rawNumericGrade = GPAUtils.getPercentTypeAverage(this, this.assignments);
|
||||
}
|
||||
if (this.grading.method == 'TOTAL_MEAN')
|
||||
{
|
||||
return this.rawNumericGrade = GPAUtils.getTotalMeanAverage(this.assignments);
|
||||
// Calculate
|
||||
if (this.grading.method == 'PERCENT_TYPE')
|
||||
{
|
||||
this._cacheNumericGrade = GPAUtils.getPercentTypeAverage(this, this.assignments);
|
||||
}
|
||||
else if (this.grading.method == 'TOTAL_MEAN')
|
||||
{
|
||||
this._cacheNumericGrade = GPAUtils.getTotalMeanAverage(this.assignments);
|
||||
}
|
||||
else this._cacheNumericGrade = -1;
|
||||
}
|
||||
|
||||
// Error
|
||||
return -1;
|
||||
return this._cacheNumericGrade;
|
||||
}
|
||||
|
||||
private _cacheAssignmentTypes: AssignmentType[];
|
||||
|
||||
/**
|
||||
* Get assignment types
|
||||
*/
|
||||
get assignmentTypes(): AssignmentType[]
|
||||
{
|
||||
if (this._cacheAssignmentTypes == null)
|
||||
{
|
||||
// Get all types
|
||||
let types = this.assignments.map(a => a.type);
|
||||
|
||||
// Remove duplicates
|
||||
types = types.filter((type, i, a) => a.indexOf(type) == i);
|
||||
|
||||
// Get total possible score for weight calculation
|
||||
let totalScoreMax = this.assignments.reduce((sum, a) => sum + a.scoreMax, 0);
|
||||
|
||||
// For every type...
|
||||
this._cacheAssignmentTypes = types.map(type =>
|
||||
{
|
||||
// Get assignments of the type
|
||||
let typeAssignments = this.assignments.filter(a => a.type == type);
|
||||
|
||||
// Count scores and max scores
|
||||
let score = typeAssignments.reduce((sum, a) => sum + a.score, 0);
|
||||
let scoreMax = typeAssignments.reduce((sum, a) => sum + a.scoreMax, 0);
|
||||
|
||||
// Calculate weight
|
||||
let weight = this.grading.method == 'PERCENT_TYPE'
|
||||
? this.grading.weightingMap[type] : scoreMax / totalScoreMax;
|
||||
|
||||
// Return
|
||||
return {name: type, id: typeAssignments[0].typeId, weight: weight,
|
||||
scoreMax: scoreMax, score: score, percent: +(score / scoreMax * 100).toFixed(2)}
|
||||
})
|
||||
}
|
||||
|
||||
return this._cacheAssignmentTypes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import Course from '@/logic/course';
|
||||
import Navigation from '@/components/navigation/navigation';
|
||||
import Constants from '@/constants';
|
||||
|
||||
const LEVEL_AP = {level: 'AP', scaleUp: 1};
|
||||
const LEVEL_H = {level: 'H', scaleUp: 0.75};
|
||||
@@ -64,4 +66,24 @@ export class CourseUtils
|
||||
// Really unknown
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the begin date of the selected term
|
||||
*/
|
||||
static getTermBeginDate()
|
||||
{
|
||||
let selected = Navigation.instance.getSelectedGradingPeriod();
|
||||
|
||||
return selected == -1 ? Constants.TERMS[0] : Constants.TERMS[selected];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end date of the selected term
|
||||
*/
|
||||
static getTermEndDate()
|
||||
{
|
||||
let selected = Navigation.instance.getSelectedGradingPeriod();
|
||||
|
||||
return selected == -1 ? Constants.TERMS[3] : Constants.TERMS[selected + 1];
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,6 @@ import moment from 'moment';
|
||||
|
||||
export class FormatUtils
|
||||
{
|
||||
/**
|
||||
* Convert date format to yyyy-mm-dd
|
||||
*
|
||||
* @param _date Date
|
||||
*/
|
||||
public static toChartDate(_date: string | Date)
|
||||
{
|
||||
// Convert to Date
|
||||
let date: Date = _date instanceof Date ? _date : new Date(_date);
|
||||
|
||||
// Convert to yyyy-mm-dd
|
||||
return moment(date).format('YYYY-MM-DD');
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit string length
|
||||
*
|
||||
@@ -1,5 +1,4 @@
|
||||
import Course from '@/logic/course';
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import Course, {Assignment} from '@/logic/course';
|
||||
|
||||
export interface Scale
|
||||
{
|
||||
@@ -136,7 +135,7 @@ export class GPAUtils
|
||||
});
|
||||
|
||||
// Return
|
||||
return score / max * 100;
|
||||
return +(score / max * 100).toFixed(2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,6 +185,6 @@ export class GPAUtils
|
||||
}
|
||||
|
||||
// Add average to the row
|
||||
return score * 100;
|
||||
return +(score * 100).toFixed(2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import Constants from '@/constants';
|
||||
|
||||
export default class GraphUtils
|
||||
{
|
||||
static DOT = '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:{color}"></span>';
|
||||
|
||||
/**
|
||||
* Base settings
|
||||
*
|
||||
* @param title
|
||||
* @param subtitle
|
||||
*/
|
||||
static getBaseSettings(title?: String, subtitle?: String)
|
||||
{
|
||||
return {
|
||||
// Color
|
||||
color: Constants.THEME.colors,
|
||||
|
||||
// Title
|
||||
title:
|
||||
{
|
||||
show: title != null,
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 13
|
||||
},
|
||||
text: title,
|
||||
subtext: subtitle,
|
||||
x: 'center'
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get term mark lines
|
||||
*/
|
||||
static getTermLines()
|
||||
{
|
||||
return {
|
||||
silent: true,
|
||||
symbol: 'none',
|
||||
lineStyle: {color: Constants.THEME.colors[2]},
|
||||
animationDuration: 500,
|
||||
data: Constants.TERMS.map((term, index) =>
|
||||
{
|
||||
return {xAxis: term.getTime(), label: {formatter: `Term ${index + 1}`}}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mark areas for percentage scores
|
||||
*/
|
||||
static getGradeMarkAreas(opacity: number)
|
||||
{
|
||||
return {
|
||||
silent: true,
|
||||
data:
|
||||
[
|
||||
// Above 100
|
||||
[{itemStyle: {color: 'rgba(230,253,255)', opacity: opacity}, yAxis: 120}, {yAxis: 100}],
|
||||
// 90 to 100
|
||||
[{itemStyle: {color: 'rgba(241,255,237)', opacity: opacity}, yAxis: 100}, {yAxis: 90}],
|
||||
// 80 to 90
|
||||
[{itemStyle: {color: 'rgba(255,250,216)', opacity: opacity}, yAxis: 90}, {yAxis: 80}],
|
||||
// 70 to 80
|
||||
[{itemStyle: {color: 'rgba(255,225,199)', opacity: opacity}, yAxis: 80}, {yAxis: 70}],
|
||||
// Below 70 (Fail)
|
||||
[{itemStyle: {color: 'rgb(255,190,184)', opacity: opacity}, yAxis: 70}, {yAxis: -100}]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import {Assignment} from '@/logic/course';
|
||||
|
||||
export default class JsonUtils
|
||||
{
|
||||
@@ -18,7 +18,7 @@ export default class JsonUtils
|
||||
type: assignment.assignment_type,
|
||||
typeId: assignment.assignment_type_id,
|
||||
description: assignment.assignment_description,
|
||||
date: new Date(assignment._date),
|
||||
time: new Date(assignment._date).getTime(),
|
||||
complete: assignment.completion_status,
|
||||
include: assignment.include_in_calculated_grade == 1,
|
||||
display: assignment.display_grade == 1,
|
||||
@@ -2,8 +2,8 @@
|
||||
<div id="assignment-type-head">
|
||||
<el-card :body-style="{padding: '0px'}">
|
||||
<div id="type-info-card">
|
||||
<span id="type-name">{{typeName}}</span>
|
||||
<span id="type-average">Average: {{average.toFixed(2)}}%</span>
|
||||
<span id="type-name">{{type.name}}</span>
|
||||
<span id="type-average">Average: {{type.percent}}%</span>
|
||||
</div>
|
||||
|
||||
<AssignmentEntry v-for="assignment of filteredAssignments" :key="assignment.id"
|
||||
@@ -16,27 +16,21 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import AssignmentEntry from '@/pages/overall/overall-course/assignment-entry/assignment-entry.vue';
|
||||
import {Assignment, AssignmentType} from '@/logic/course';
|
||||
|
||||
@Component({
|
||||
components: {AssignmentEntry}
|
||||
})
|
||||
export default class AssignmentTypeHead extends Vue
|
||||
{
|
||||
@Prop({required: true}) typeName: string;
|
||||
@Prop({required: true}) type: AssignmentType;
|
||||
@Prop({required: true}) assignments: Assignment[];
|
||||
|
||||
get filteredAssignments()
|
||||
{
|
||||
// Filter assignments to only this type
|
||||
return this.assignments.filter(a => a.complete == 'Complete' && a.type == this.typeName);
|
||||
}
|
||||
|
||||
get average()
|
||||
{
|
||||
return this.filteredAssignments.reduce((a, b) => a + b.score, 0) /
|
||||
this.filteredAssignments.reduce((a, b) => a + b.scoreMax, 0) * 100;
|
||||
return this.assignments.filter(a => a.typeId == this.type.id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -14,8 +14,20 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<AssignmentTypeHead v-for="type in getAssignmentTypes()" :key="type"
|
||||
:type-name="type" :assignments="course.assignments">
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-card class="large overall-line-card vertical-center" body-style="padding: 0">
|
||||
<CourseTypeRadar :course="course"></CourseTypeRadar>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
Hi
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<AssignmentTypeHead v-for="type in course.assignmentTypes" :key="type.id"
|
||||
:type="type" :assignments="course.assignments">
|
||||
</AssignmentTypeHead>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -23,15 +35,15 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import CourseHead from '@/pages/overall/overall-course/course-head/course-head.vue';
|
||||
import CourseScatter from '@/pages/course/course-scatter/course-scatter';
|
||||
import AssignmentEntry from '@/pages/overall/overall-course/assignment-entry/assignment-entry.vue';
|
||||
import AssignmentTypeHead from '@/pages/course/assignment-type-head/assignment-type-head.vue';
|
||||
import Course from '@/logic/course';
|
||||
import Course, {Assignment} from '@/logic/course';
|
||||
import TypeRadar from '@/pages/course/course-type-radar/type-radar';
|
||||
|
||||
@Component({
|
||||
components: {AssignmentEntry, CourseHead, CourseScatter, AssignmentTypeHead}
|
||||
components: {CourseTypeRadar: TypeRadar, AssignmentEntry, CourseHead, CourseScatter, AssignmentTypeHead}
|
||||
})
|
||||
export default class CoursePage extends Vue
|
||||
{
|
||||
@@ -52,21 +64,6 @@
|
||||
}
|
||||
else return this.unread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the types of the assignments.
|
||||
*/
|
||||
getAssignmentTypes(): string[]
|
||||
{
|
||||
// Get all types
|
||||
let types = this.course.assignments.map(a => a.type);
|
||||
|
||||
// Remove duplicates
|
||||
types = types.filter((type, i, a) => a.indexOf(type) == i);
|
||||
|
||||
// Return it
|
||||
return types;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import Constants from '@/constants';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
import {FormatUtils} from '@/logic/utils/format-utils';
|
||||
import moment from 'moment';
|
||||
import Course from '@/logic/course';
|
||||
import Course, {Assignment} from '@/logic/course';
|
||||
import GraphUtils from '@/logic/utils/graph-utils';
|
||||
|
||||
@Component({
|
||||
})
|
||||
export default class CourseScatter extends Vue
|
||||
{
|
||||
private static DOT = '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:{color}"></span>';
|
||||
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
/**
|
||||
@@ -28,40 +26,11 @@ export default class CourseScatter extends Vue
|
||||
*/
|
||||
get chartSettings()
|
||||
{
|
||||
// Map assignments
|
||||
let map = this.mapAssignments();
|
||||
|
||||
// Scatter data point style
|
||||
let itemStyle =
|
||||
{
|
||||
normal:
|
||||
{
|
||||
opacity: 0.8,
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.2)'
|
||||
}
|
||||
};
|
||||
|
||||
// Create settings
|
||||
let settings =
|
||||
{
|
||||
// Color
|
||||
color: Constants.THEME.colors,
|
||||
|
||||
// Title
|
||||
title:
|
||||
{
|
||||
show: true,
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 13
|
||||
},
|
||||
text: 'Assignments',
|
||||
subtext: 'Assignment scores for ' + this.course.name,
|
||||
x: 'center'
|
||||
},
|
||||
// Base settings
|
||||
...GraphUtils.getBaseSettings('Assignments', 'Assignment scores for ' + this.course.name),
|
||||
|
||||
// X axis represents course names
|
||||
xAxis:
|
||||
@@ -71,7 +40,7 @@ export default class CourseScatter extends Vue
|
||||
{
|
||||
formatter: (name: any) => moment(name).format('MMM DD')
|
||||
},
|
||||
max: FormatUtils.toChartDate(new Date())
|
||||
max: new Date().getTime()
|
||||
},
|
||||
|
||||
// Y axis represents GPAs and MaxGPAs
|
||||
@@ -85,8 +54,8 @@ export default class CourseScatter extends Vue
|
||||
{
|
||||
formatter: (name: any) => name + '%'
|
||||
},
|
||||
max: 100,
|
||||
min: (value: any) => Math.floor(value.min) - 5
|
||||
min: (value: any) => Math.floor(value.min) - 5,
|
||||
max: (value: any) => Math.min(Math.ceil(value.max), 110)
|
||||
},
|
||||
|
||||
// Tooltip
|
||||
@@ -97,8 +66,8 @@ export default class CourseScatter extends Vue
|
||||
{
|
||||
type: 'cross'
|
||||
},
|
||||
formatter: (ps: any[]) => ps[0].data[0] + '<br>' + ps.map(p =>
|
||||
`${CourseScatter.DOT.replace('{color}', p.color)}
|
||||
formatter: (ps: any[]) => moment(ps[0].data[0]).format('MMM DD, YYYY') + '<br>' + ps.map(p =>
|
||||
`${GraphUtils.DOT.replace('{color}', p.color)}
|
||||
${FormatUtils.limit(p.data[2], 22)}: ${p.data[1]}%<br>`).join('')
|
||||
},
|
||||
|
||||
@@ -115,20 +84,53 @@ export default class CourseScatter extends Vue
|
||||
},
|
||||
|
||||
// Data
|
||||
series: Array.from(map, ([type, assignments]) =>
|
||||
{
|
||||
return {
|
||||
type: 'scatter',
|
||||
name: type,
|
||||
data: CourseScatter.assignmentsData(assignments),
|
||||
itemStyle: itemStyle
|
||||
}
|
||||
})
|
||||
series: this.series()
|
||||
};
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get series data
|
||||
*/
|
||||
private series()
|
||||
{
|
||||
// Scatter data point style
|
||||
let itemStyle =
|
||||
{
|
||||
normal:
|
||||
{
|
||||
opacity: 0.8,
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.2)'
|
||||
}
|
||||
};
|
||||
|
||||
// Create scatter plots
|
||||
let map = this.mapAssignments();
|
||||
let series: any[] = Array.from(map, ([type, assignments]) =>
|
||||
{
|
||||
return {
|
||||
type: 'scatter',
|
||||
name: type,
|
||||
data: CourseScatter.assignmentsData(assignments),
|
||||
itemStyle: itemStyle
|
||||
}
|
||||
});
|
||||
|
||||
// Push other stuff
|
||||
series.push(
|
||||
{
|
||||
type: 'line',
|
||||
markLine: GraphUtils.getTermLines(),
|
||||
markArea: GraphUtils.getGradeMarkAreas(0.4)
|
||||
});
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map assignments to {assignmentType, [assignment]} format.
|
||||
*/
|
||||
@@ -158,6 +160,6 @@ export default class CourseScatter extends Vue
|
||||
private static assignmentsData(assignments: Assignment[])
|
||||
{
|
||||
return assignments.filter(a => a.complete == 'Complete')
|
||||
.map(a => [FormatUtils.toChartDate(a.date), (a.score / a.scoreMax * 100).toFixed(2), a.description]);
|
||||
.map(a => [a.time, (a.score / a.scoreMax * 100).toFixed(2), a.description]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="course-scatter">
|
||||
<ve-scatter height="450px" class="graph" :extend="{heyIUsedCourseObject: this.course.name}" :after-config="afterConfig"></ve-scatter>
|
||||
<ve-scatter height="450px" class="graph" :extend="{a: this.course.name}" :after-config="afterConfig"></ve-scatter>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import Constants from '@/constants';
|
||||
import {FormatUtils} from '@/logic/utils/format-utils';
|
||||
import moment from 'moment';
|
||||
import Course, {Assignment} from '@/logic/course';
|
||||
import GraphUtils from '@/logic/utils/graph-utils';
|
||||
|
||||
@Component
|
||||
export default class TypeRadar extends Vue
|
||||
{
|
||||
@Prop({required: true}) course: Course;
|
||||
|
||||
/**
|
||||
* Override options
|
||||
*
|
||||
* @param options Original options (Unused)
|
||||
*/
|
||||
afterConfig(options: any)
|
||||
{
|
||||
return this.chartSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate settings
|
||||
*/
|
||||
get chartSettings()
|
||||
{
|
||||
// Create settings
|
||||
let settings =
|
||||
{
|
||||
...GraphUtils.getBaseSettings('Type Radar',
|
||||
'Assignment type grades for ' + this.course.name),
|
||||
|
||||
// Radar settings
|
||||
radar:
|
||||
{
|
||||
// shape: 'circle',
|
||||
name:
|
||||
{
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 14,
|
||||
textShadowColor: '#cfcfcf',
|
||||
textShadowBlur: 2,
|
||||
textShadowOffsetX: 1,
|
||||
textShadowOffsetY: 1,
|
||||
color: '#fff',
|
||||
backgroundColor: '#f6f6f6',
|
||||
borderRadius: 3,
|
||||
padding: [3, 5]
|
||||
}
|
||||
},
|
||||
splitArea:
|
||||
{
|
||||
areaStyle:
|
||||
{
|
||||
color:
|
||||
[
|
||||
'rgb(255,161,151)',
|
||||
'rgb(255,190,184)',
|
||||
'rgba(255,225,199)',
|
||||
'rgba(255,250,216)',
|
||||
'rgba(241,255,237)',
|
||||
],
|
||||
opacity: 0.4
|
||||
}
|
||||
},
|
||||
indicator: this.course.assignmentTypes.map((t, i) => {return {
|
||||
name: `${t.name}\n${t.percent}%`,
|
||||
max: 100,
|
||||
color: Constants.THEME.colors[i]
|
||||
}}),
|
||||
radius: '60%',
|
||||
center: ['50%', '60%']
|
||||
},
|
||||
|
||||
tooltip: {},
|
||||
|
||||
// Data
|
||||
series:
|
||||
{
|
||||
type: 'radar',
|
||||
data:
|
||||
[
|
||||
{
|
||||
name: 'Score',
|
||||
symbol: 'circle',
|
||||
areaStyle:
|
||||
{
|
||||
color:
|
||||
{
|
||||
type: 'radial',
|
||||
x: 0.5, y: 0.6, r: 0.5,
|
||||
colorStops:
|
||||
[
|
||||
{offset: 0, color: '#ffa0a0'},
|
||||
{offset: 0.5, color: '#fffead'},
|
||||
{offset: 1, color: '#d1ffde'}
|
||||
],
|
||||
global: false // 缺省为 false
|
||||
},
|
||||
opacity: 0.2
|
||||
},
|
||||
value: this.course.assignmentTypes.map(t => t.percent)
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
color: '#6771c1'
|
||||
};
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div id="course-scatter">
|
||||
<ve-radar height="450px" class="graph" :extend="{a: this.course.name}" :after-config="afterConfig"></ve-radar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./type-radar.ts" lang="ts"></script>
|
||||
<style lang="scss" scoped>
|
||||
#overall-bar
|
||||
{
|
||||
.graph
|
||||
{
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,8 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import Course from '@/logic/course';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import {GPAUtils} from '@/logic/utils/gpa-utils';
|
||||
import Constants from '@/constants';
|
||||
import {FormatUtils} from '@/utils/format-utils';
|
||||
import {FormatUtils} from '@/logic/utils/format-utils';
|
||||
|
||||
@Component({
|
||||
})
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
<el-row class="unread-row">
|
||||
<el-col :span="3" class="date">
|
||||
<span class="month">{{getMoment(assignment.date).format("MMM D")}}</span>
|
||||
<span class="now">({{getMoment(assignment.date).fromNow()}})</span>
|
||||
<span class="month">{{getMoment(assignment.time).format("MMM D")}}</span>
|
||||
<span class="now">({{getMoment(assignment.time).fromNow()}})</span>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="15" class="description">
|
||||
@@ -36,8 +36,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import {Assignment} from '@/components/app/app';
|
||||
import moment from 'moment';
|
||||
import {Assignment} from '@/logic/course';
|
||||
|
||||
@Component
|
||||
export default class AssignmentEntry extends Vue
|
||||
@@ -53,7 +53,7 @@
|
||||
*
|
||||
* @param date Date
|
||||
*/
|
||||
getMoment(date: string)
|
||||
getMoment(date: number)
|
||||
{
|
||||
return moment(new Date(date));
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<script lang="ts">
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import Course from '@/logic/course';
|
||||
import {CourseUtils} from '@/utils/course-utils';
|
||||
import {CourseUtils} from '@/logic/utils/course-utils';
|
||||
import Navigation from '@/components/navigation/navigation';
|
||||
|
||||
@Component
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import App, {Assignment} from '@/components/app/app';
|
||||
import App from '@/components/app/app';
|
||||
import AssignmentEntry from '@/pages/overall/overall-course/assignment-entry/assignment-entry.vue';
|
||||
import CourseHead from '@/pages/overall/overall-course/course-head/course-head.vue';
|
||||
import Course from '@/logic/course';
|
||||
import Course, {Assignment} from '@/logic/course';
|
||||
|
||||
@Component({
|
||||
components: {UnreadEntry: AssignmentEntry, CourseHead}
|
||||
|
||||
@@ -1,224 +1,164 @@
|
||||
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||
import moment from 'moment';
|
||||
import Course from '@/logic/course';
|
||||
import Course, {Assignment} from '@/logic/course';
|
||||
import Constants from '@/constants';
|
||||
import Navigation from '@/components/navigation/navigation';
|
||||
import {CourseUtils} from '@/logic/utils/course-utils';
|
||||
import GraphUtils from '@/logic/utils/graph-utils';
|
||||
import {GPAUtils} from '@/logic/utils/gpa-utils';
|
||||
|
||||
@Component({
|
||||
})
|
||||
@Component
|
||||
export default class OverallLine extends Vue
|
||||
{
|
||||
@Prop({required: true}) courses: Course[];
|
||||
|
||||
private settings =
|
||||
{
|
||||
// Title
|
||||
title:
|
||||
{
|
||||
show: true,
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 12
|
||||
},
|
||||
text: 'Average Grade',
|
||||
subtext: 'Average score trend for every course',
|
||||
x: 'center'
|
||||
},
|
||||
// Legend
|
||||
legend:
|
||||
{
|
||||
show: false,
|
||||
textStyle:
|
||||
{
|
||||
fontSize: 11
|
||||
},
|
||||
icon: 'circle'
|
||||
},
|
||||
// Zoom bar
|
||||
dataZoom:
|
||||
[
|
||||
{
|
||||
startValue: moment().subtract(30, 'days').format('M/D/YYYY')
|
||||
},
|
||||
{
|
||||
type: 'inside'
|
||||
}
|
||||
],
|
||||
series:
|
||||
{
|
||||
smooth: true,
|
||||
|
||||
// Mark area
|
||||
markArea:
|
||||
{
|
||||
silent: true,
|
||||
data:
|
||||
[
|
||||
// Above 100
|
||||
[
|
||||
{
|
||||
yAxis: 120,
|
||||
itemStyle: {color: 'rgba(230,253,255,0.09)'}
|
||||
}, {yAxis: 100}
|
||||
],
|
||||
// 90 to 100
|
||||
[
|
||||
{
|
||||
yAxis: 100,
|
||||
itemStyle: {color: 'rgba(241,255,237,0.09)'}
|
||||
}, {yAxis: 90}
|
||||
],
|
||||
// 80 to 90
|
||||
[
|
||||
{
|
||||
yAxis: 90,
|
||||
itemStyle: {color: 'rgba(255,250,216,0.09)'}
|
||||
}, {yAxis: 80}
|
||||
],
|
||||
// 70 to 80
|
||||
[
|
||||
{
|
||||
yAxis: 80,
|
||||
itemStyle: {color: 'rgba(255,225,199,0.1)'}
|
||||
}, {yAxis: 70}
|
||||
],
|
||||
// Below 70 (Fail)
|
||||
[
|
||||
{
|
||||
yAxis: 70,
|
||||
itemStyle: {color: 'rgb(255,190,184, 0.09)'}
|
||||
}, {yAxis: -100}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
xAxis:
|
||||
{
|
||||
//type: 'time'
|
||||
},
|
||||
yAxis:
|
||||
{
|
||||
min: (value: any) => Math.floor(value.min),
|
||||
max: (value: any) => Math.min(value.max, 110)
|
||||
}
|
||||
};
|
||||
|
||||
chartCache: any;
|
||||
filteredCourses: Course[];
|
||||
settings: any;
|
||||
|
||||
/**
|
||||
* Convert assignments list to a graph dataset.
|
||||
* When this component is created
|
||||
*/
|
||||
get convertChart()
|
||||
created()
|
||||
{
|
||||
// Caching
|
||||
if (this.chartCache != undefined) return this.chartCache;
|
||||
// Filter courses
|
||||
this.filteredCourses = this.courses.filter(c => c.isGraded && c.assignments.length > 0);
|
||||
|
||||
let courses = this.courses.filter(c => c.assignments.length > 0);
|
||||
// Generate settings
|
||||
this.settings =
|
||||
{
|
||||
...GraphUtils.getBaseSettings('Average Grade', 'Average score trend for every course'),
|
||||
|
||||
// Compute the column names
|
||||
let columns = courses.map(course => course.name);
|
||||
columns.unshift('date');
|
||||
// Zoom bar
|
||||
dataZoom:
|
||||
[
|
||||
{
|
||||
type: 'slider',
|
||||
startValue: Math.max(moment().subtract(30, 'days').toDate().getTime(),
|
||||
CourseUtils.getTermBeginDate().getTime()),
|
||||
|
||||
// Minimum zoom: 1 week
|
||||
minValueSpan: 7 * 24 * 60 * 60 * 1000
|
||||
}
|
||||
],
|
||||
|
||||
// Tooltip
|
||||
tooltip:
|
||||
{
|
||||
trigger: 'axis'
|
||||
},
|
||||
|
||||
// Axis
|
||||
xAxis:
|
||||
{
|
||||
type: 'time',
|
||||
axisLabel:
|
||||
{
|
||||
formatter: (name: any) => moment(name).format('MMM DD')
|
||||
},
|
||||
},
|
||||
yAxis:
|
||||
{
|
||||
axisLabel:
|
||||
{
|
||||
formatter: (name: any) => name + '%'
|
||||
},
|
||||
min: (value: any) => Math.floor(value.min),
|
||||
max: (value: any) => Math.min(Math.ceil(value.max), 110)
|
||||
},
|
||||
|
||||
// Series data
|
||||
series: this.series()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override options
|
||||
*
|
||||
* @param options Original options (Unused)
|
||||
*/
|
||||
afterConfig(options: any)
|
||||
{
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate series data
|
||||
*/
|
||||
private series()
|
||||
{
|
||||
// Each course
|
||||
let series: any[] = this.filteredCourses.map(course => this.getCourseSeries(course));
|
||||
|
||||
// Push other stuff
|
||||
series.push(
|
||||
{
|
||||
type: 'line',
|
||||
markLine: GraphUtils.getTermLines(),
|
||||
markArea: GraphUtils.getGradeMarkAreas(0.4)
|
||||
});
|
||||
|
||||
return series
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate series data for a course
|
||||
*
|
||||
* @param course
|
||||
*/
|
||||
private getCourseSeries(course: Course)
|
||||
{
|
||||
// Graded assignments
|
||||
let assignments = course.assignments.slice().reverse();
|
||||
|
||||
// Create series
|
||||
return {
|
||||
name: course.name,
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle', // circle, diamond, emptyCircle, none
|
||||
data: this.toDateRange([...assignments.map(a => a.time)].map((time, i) =>
|
||||
{
|
||||
// Find subset before this assignment
|
||||
let subset = assignments.filter(a => a.time <= time);
|
||||
|
||||
// Find grade
|
||||
if (course.grading.method == 'PERCENT_TYPE')
|
||||
return [time, GPAUtils.getPercentTypeAverage(course, subset)];
|
||||
if (course.grading.method == 'TOTAL_MEAN')
|
||||
return [time, GPAUtils.getTotalMeanAverage(subset)];
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert point data to date range data.
|
||||
* Eg. [[Mon, 10], [Wed, 5]] to [[Mon, 10], [Tue, 10], [Wed, 5]]
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
private toDateRange(data: any[])
|
||||
{
|
||||
// Find the min date
|
||||
let minDates = courses.map(course => course.assignments[course.assignments.length - 1].date.getTime());
|
||||
let minDates = this.courses.map(course => course.assignments[course.assignments.length - 1].time);
|
||||
let minDate: Date = new Date(Math.min.apply(null, minDates));
|
||||
|
||||
// Find the dates in between
|
||||
let now = new Date();
|
||||
let dates = [];
|
||||
let now = new Date(Math.min(new Date().getTime(), CourseUtils.getTermEndDate().getTime()));
|
||||
let dates: number[] = [];
|
||||
for (let date = minDate; date <= now; date.setDate(date.getDate() + 1))
|
||||
{
|
||||
dates.push(new Date(date));
|
||||
dates.push(new Date(date).getTime());
|
||||
}
|
||||
|
||||
// Compute the rows data
|
||||
let rows: {[index: string]: any}[] = [];
|
||||
dates.forEach(date =>
|
||||
let lastValue: any = null;
|
||||
return dates.map(date =>
|
||||
{
|
||||
// Define row object
|
||||
let row: {[index: string]:any} = {'date': date.toLocaleDateString('en-US')};
|
||||
// Data point on this specific date
|
||||
let thisValue = data.find(a => a[0] == date);
|
||||
|
||||
// Loop through courses
|
||||
courses.forEach(course =>
|
||||
{
|
||||
// Total Mean
|
||||
if (course.grading.method == 'TOTAL_MEAN')
|
||||
{
|
||||
let score = 0;
|
||||
let max = 0;
|
||||
|
||||
// Loop through assignments
|
||||
course.assignments.forEach(assignment =>
|
||||
{
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') return;
|
||||
|
||||
// Date is being looked at
|
||||
if (assignment.date.getTime() < date.getTime())
|
||||
{
|
||||
// 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} = {};
|
||||
|
||||
// Loop through assignments
|
||||
course.assignments.forEach(assignment =>
|
||||
{
|
||||
// If assignment should be displayed
|
||||
if (assignment.complete != 'Complete') return;
|
||||
|
||||
// Date is being looked at
|
||||
if (assignment.date.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
|
||||
rows.push(row);
|
||||
// None
|
||||
if (thisValue == null) return [date, lastValue == null ? null : lastValue[1]];
|
||||
else return [date, (lastValue = thisValue)[1]];
|
||||
});
|
||||
|
||||
return this.chartCache =
|
||||
{
|
||||
columns: columns,
|
||||
rows: rows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div id="overall-line">
|
||||
<ve-line :data="convertChart" :extend="settings"></ve-line>
|
||||
<ve-line :extend="{a: this.courses}" :after-config="afterConfig"></ve-line>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
import OverallBar from '@/pages/overall/overall-bar/overall-bar';
|
||||
import OverallCourse from '@/pages/overall/overall-course/overall-course';
|
||||
import Course from '@/logic/course';
|
||||
import {GPAUtils} from '@/utils/gpa-utils';
|
||||
import {GPAUtils} from '@/logic/utils/gpa-utils';
|
||||
|
||||
@Component({
|
||||
components: {OverallLine, OverallBar, OverallCourse}
|
||||
|
||||
Reference in New Issue
Block a user