Compare commits

...

121 Commits

Author SHA1 Message Date
Hykilpikonna b5b9f14a49 [U] Pre-release v0.2.2.303 2019-09-30 21:16:53 -04:00
Hykilpikonna fc93cd1248 [+] Skip if not graded 2019-09-30 21:05:39 -04:00
Hykilpikonna 6e58c634a1 [F] Correct course average grade graph calculation 2019-09-30 21:05:29 -04:00
Hykilpikonna 10cca344c7 [M] Copy old course calculation method to deprecated 2019-09-30 21:05:07 -04:00
Hykilpikonna cdf58ea3a0 [O] Specify type for weighting map 2019-09-30 21:04:47 -04:00
Hykilpikonna d97c80afbb [U] Update course model to match the update 2019-09-30 19:25:59 -04:00
Hykilpikonna 8c7d028f5b [R] Update screenshot 2019-09-29 19:51:19 -04:00
Hykilpikonna 19ba8ec7e6 [U] Pre-release v0.2.1.295 2019-09-29 19:45:22 -04:00
Hykilpikonna 6937952f3a [O] Optimize alignment 2019-09-29 19:44:38 -04:00
Hykilpikonna 485717d3ef [+] Dynamic min value 2019-09-29 19:42:26 -04:00
Hykilpikonna 3f3e46bec5 [O] Make graph-overall smoother 2019-09-29 19:42:16 -04:00
Hykilpikonna 1494315e0c [O] Optimize wording 2019-09-29 19:14:39 -04:00
Hykilpikonna 76eb8168ce [O] Align the two graph titles 2019-09-29 19:14:27 -04:00
Hykilpikonna e4a1c4973d [O] Rotate text to 90 degrees 2019-09-29 19:14:12 -04:00
Hykilpikonna b22fc41582 [+] Add title to course gpa graph 2019-09-29 19:14:02 -04:00
Hykilpikonna 0e1264d938 [O] Make the full grade color gray 2019-09-29 18:29:52 -04:00
Hykilpikonna a94a6db4b7 [O] Make the bar color match the line graph color 2019-09-29 18:29:32 -04:00
Hykilpikonna 18b5b7de77 [O] Separate the generation of the gp dataset 2019-09-29 18:28:33 -04:00
Hykilpikonna 28d4e5d871 [F] Fix typo on assignment sign 2019-09-29 18:09:32 -04:00
Hykilpikonna cc5b7b2793 [O] Remove dataset, move data to series 2019-09-29 18:02:40 -04:00
Hykilpikonna 7b4ced4e94 [O] Display larger data on farthest layer 2019-09-29 17:46:00 -04:00
Hykilpikonna 70e6debe82 [+] Stack the bars in graph 2019-09-29 17:44:08 -04:00
Hykilpikonna e0c4426ebb [O] Optimize series generation with [].map 2019-09-29 17:43:01 -04:00
Hykilpikonna 495e776971 [O] Optimize code length 2019-09-29 17:39:21 -04:00
Hykilpikonna fc625c8edb [+] Truncate text if too long 2019-09-29 17:38:53 -04:00
Hykilpikonna 6c1623cec5 [+] Show axis label 2019-09-29 17:31:37 -04:00
Hykilpikonna a9a9cf5f11 [O] Make bar graph vertical 2019-09-29 17:31:29 -04:00
Hykilpikonna d8f276005d [O] Make the two graphs the same width 2019-09-29 17:20:07 -04:00
Hykilpikonna 5c9e67478f [+] Rebuild entire average graph from settings 2019-09-29 17:17:23 -04:00
Hykilpikonna 5e0394f11c [+] Create a theme constant 2019-09-29 16:28:44 -04:00
Hykilpikonna 774e436c9c [F] Fix typo in :data bind method 2019-09-29 16:14:17 -04:00
Hykilpikonna 8b653e3317 [+] GraphAverage: Return graph 2019-09-29 16:13:57 -04:00
Hykilpikonna 9731d4e380 [+] GraphAverage: Map GPA and MaxGPA for each course 2019-09-29 16:13:52 -04:00
Hykilpikonna e3ca21ec29 [+] GraphAverage: Make column list 2019-09-29 16:13:14 -04:00
Hykilpikonna a89c29dc53 [F] Fix class name typo 2019-09-29 16:04:48 -04:00
Hykilpikonna 87a59416ec [O] Optimize minDate calculation with array.map() and Math.min 2019-09-29 16:04:38 -04:00
Hykilpikonna 8243312179 [O] Optimize column generation with array.map() 2019-09-29 15:48:28 -04:00
Hykilpikonna 3dcc1b8319 [+] Add zome bar 2019-09-29 15:34:17 -04:00
Hykilpikonna 899ff22677 [O] Hide legend for now 2019-09-29 15:33:14 -04:00
Hykilpikonna 788f51399a [O] Center title 2019-09-29 15:33:05 -04:00
Hykilpikonna 575dbdf765 [-] Remove duplicate title in html 2019-09-29 15:32:58 -04:00
Hykilpikonna 1659964011 [O] Show title in graph settings 2019-09-29 15:32:46 -04:00
Hykilpikonna 02533f6b07 [+] Add graph-average to overall page 2019-09-29 15:23:51 -04:00
Hykilpikonna 68a91e5eb8 [+] Create average graphs 2019-09-29 15:18:46 -04:00
Hykilpikonna 1eb6128aac [O] Optimize type declaration 2019-09-29 15:17:14 -04:00
Hykilpikonna fbd59f9ec3 [-] Remove unnecessary var declaration in getGPA() 2019-09-29 15:12:47 -04:00
Hykilpikonna 7eb7838f66 [M] Move convertChart() to graph-overall.ts 2019-09-29 15:12:05 -04:00
Hykilpikonna 6459e1c09b [O] Make the legend icon a circle 2019-09-29 15:09:15 -04:00
Hykilpikonna 4461ef835f [U] Pass in filtered courses instead 2019-09-29 15:08:30 -04:00
Hykilpikonna 6ee207d754 [+] Filter courses after assignments are retrieved 2019-09-29 15:07:01 -04:00
Hykilpikonna 3c0c0d5f1b [-] Remove unnecessary jsdoc @param declaration 2019-09-29 15:02:24 -04:00
Hykilpikonna bc8fcc021d [M] Move course filtering to course-utils.ts 2019-09-29 15:01:47 -04:00
Hykilpikonna e9a6cfe5c5 [O] Make legend font size smaller 2019-09-29 14:47:47 -04:00
Hykilpikonna 0e92cedf54 [+] Add title but doesn't show 2019-09-29 14:27:25 -04:00
Hykilpikonna a73d6b0399 Revert "[O] Use vue-echarts instead of v-charts"
This reverts commit d254706a21.
2019-09-29 12:45:06 -04:00
Hykilpikonna 8042765ff5 Revert "[-] Remove v-charts dependency"
This reverts commit 8978b7ca7b.
2019-09-29 12:45:03 -04:00
Hykilpikonna 74a033fb5d Revert "[+] Import vue-echarts"
This reverts commit b78c5b4f5b.
2019-09-29 12:44:57 -04:00
Hykilpikonna cc4378d905 Revert "[+] Add vue-echarts transpile dependencies"
This reverts commit a526946b83.
2019-09-29 12:44:55 -04:00
Hykilpikonna 0fd3696342 Revert "[U] Update overall graph component"
This reverts commit c262584e2f.
2019-09-29 12:44:52 -04:00
Hykilpikonna 3de4b1f6e6 Revert "[+] Add a graph title"
This reverts commit 610d1d190d.
2019-09-29 12:44:50 -04:00
Hykilpikonna 610d1d190d [+] Add a graph title 2019-09-28 17:55:44 -04:00
Hykilpikonna c262584e2f [U] Update overall graph component 2019-09-28 17:55:35 -04:00
Hykilpikonna a526946b83 [+] Add vue-echarts transpile dependencies 2019-09-28 17:52:32 -04:00
Hykilpikonna b78c5b4f5b [+] Import vue-echarts 2019-09-28 17:50:42 -04:00
Hykilpikonna 8978b7ca7b [-] Remove v-charts dependency 2019-09-28 17:47:18 -04:00
Hykilpikonna d254706a21 [O] Use vue-echarts instead of v-charts 2019-09-28 17:47:02 -04:00
Hykilpikonna 8dd7d35abe [U] Pre-release v0.1.2.236 2019-09-28 17:38:33 -04:00
Hykilpikonna 2b989fb137 [-] Remove placeholder button 2019-09-28 17:35:55 -04:00
Hykilpikonna e2a13e90e0 [O] Optimize font size for max gpa 2019-09-28 17:35:46 -04:00
Hykilpikonna 3546a57711 [O] Use class to add style to card 2019-09-28 17:35:17 -04:00
Hykilpikonna 770e26b0cf [F] Fix warning: " should be ' 2019-09-28 16:33:00 -04:00
Hykilpikonna fff60f5754 [F] Fix the newline display for button 2019-09-27 20:12:41 -04:00
Hykilpikonna a46e011c90 [O] Use css to produce new-line effect instead of <br> 2019-09-27 16:47:22 -04:00
Hykilpikonna 48224d9e34 [O] Optimize jsdocs 2019-09-27 16:40:59 -04:00
Hykilpikonna eb311e9f2d [+] Display the maximum GPA 2019-09-27 16:38:52 -04:00
Hykilpikonna 7b53e65a1b [O] Return raw GPA in overall 2019-09-27 16:38:39 -04:00
Hykilpikonna 065d6d31a3 [O] Hit enter to login 2019-09-27 16:38:26 -04:00
Hykilpikonna d8eb160123 [F] Fix max gpa calculation 2019-09-27 16:33:50 -04:00
Hykilpikonna 8e924d8e33 [+] Also calculate max gpa when calculating gpa 2019-09-24 22:22:18 -04:00
Hykilpikonna 1e45b418b8 [O] Encapsulate getGP() with letter grade passed in 2019-09-24 22:20:58 -04:00
Hykilpikonna e42c8be76f [O] Detect letter grade for presence instead of numeric grade 2019-09-24 22:19:48 -04:00
Hykilpikonna b6b3f921a2 [R] Recreate license 2019-09-24 07:37:09 -04:00
Hykilpikonna d3234be6db [-] Remove license 2019-09-24 07:35:37 -04:00
Hykilpikonna 50a5f9dcc5 [O] Remove todo that is already done 2019-09-23 21:11:27 -04:00
Hykilpikonna 62d40dd0a7 [+] Add cname 2019-09-23 20:14:49 -04:00
Hykilpikonna 9e8e44b91f [-] Remove public path 2019-09-23 20:14:41 -04:00
Hykilpikonna df85e1084f [+] Create deploy script
https://cli.vuejs.org/guide/deployment.html#github-pages
2019-09-23 19:58:02 -04:00
Hykilpikonna 0b41f8ac5d [+] Add deploy path 2019-09-23 19:56:27 -04:00
Hykilpikonna 7b11537e0f [R] Make title shorter 2019-09-23 19:52:00 -04:00
Hykilpikonna 830f55441c [R] Create License
[R] Create License
2019-09-23 19:50:57 -04:00
Hykilpikonna 0a49d791ae [R] Create License 2019-09-23 19:50:35 -04:00
Hykilpikonna c675fc5650 [M] Merge Feature > Master: First release
[M] Merge Feature > Master: First release
2019-09-23 19:49:33 -04:00
Hykilpikonna ad8496e661 [R] Add license 2019-09-23 19:47:16 -04:00
Hykilpikonna 72b9e4d214 [R] Write introduction 2019-09-23 19:46:31 -04:00
Hykilpikonna fa3fc44526 [U] Update version number 2019-09-23 19:41:49 -04:00
Hykilpikonna 419cced592 [R] Add a title 2019-09-23 19:37:58 -04:00
Hykilpikonna 352fd58fb5 [O] Show the graph with minimum y axis at 70 2019-09-23 19:33:27 -04:00
Hykilpikonna f043d77e5e [M] Move graph title to the graph component 2019-09-23 19:32:37 -04:00
Hykilpikonna 9e88f3efe1 [U] Ignore 0 grades in calculations 2019-09-23 19:32:21 -04:00
Hykilpikonna cadd328958 [O] Separate settings 2019-09-20 19:27:10 -04:00
Hykilpikonna 8c4759d497 [-] Remove unnecessary sample chart data 2019-09-20 19:26:23 -04:00
Hykilpikonna ac8f488ab4 [O] Refresh page after signing out 2019-09-20 19:21:07 -04:00
Hykilpikonna df639a360c [+] Implement actual sign out function 2019-09-20 19:10:37 -04:00
Hykilpikonna 24328508c0 [+] Bind sign out event 2019-09-20 19:10:23 -04:00
Hykilpikonna ebd4849703 [O] Optimize sign out button 2019-09-20 19:10:12 -04:00
Hykilpikonna 80d72bb5c0 Merge branch 'styling' into feature 2019-09-20 18:52:56 -04:00
Hykilpikonna 204e7dc04a [F] Move signOut() to the right place 2019-09-20 18:50:13 -04:00
Hykilpikonna cef0b31dba [O] Only display score when it is complete 2019-09-19 19:11:54 -04:00
Hykilpikonna 22ba3acab0 [F] Fix key word typo 2019-09-19 19:11:00 -04:00
Hykilpikonna 88bdcd8cf8 [O] Optimize code length 2019-09-19 18:58:59 -04:00
Hykilpikonna 8559a93b9c [+] Filter courses to a variable 2019-09-19 18:58:00 -04:00
Hykilpikonna f8db85dca3 [F] Fix getter signature problem 2019-09-19 18:57:47 -04:00
Hykilpikonna b4bd24d884 [+] Filter courses without assignments 2019-09-19 18:55:55 -04:00
Hykilpikonna f6d30a750a [+] Filter courses without levels 2019-09-19 18:55:44 -04:00
Hykilpikonna 988d6f5463 [+] Create method to filter out courses 2019-09-19 18:55:34 -04:00
Hykilpikonna 608132ad6c [O] Reduce code length 2019-09-19 18:49:00 -04:00
Hykilpikonna 6cffb745a7 [O] Specify type with ts-ignore 2019-09-19 18:48:17 -04:00
Hykilpikonna 82212cf10c [O] Fix " warning 2019-09-19 18:46:40 -04:00
Hykilpikonna 08b665a1fa [+] Call custom event to sign out 2019-09-18 23:25:25 -04:00
Hykilpikonna 500e13ef9d [+] Add onclick to sign-out 2019-09-18 23:25:12 -04:00
Hykilpikonna b95220ba1a [+] Add sign out button 2019-09-18 23:23:24 -04:00
23 changed files with 668 additions and 172 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 HyDEV
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+39 -12
View File
@@ -1,6 +1,38 @@
# veracross-analyzer
<h1 align="center"><br><br>
VeracrossAnalyzer UI
</h1>
## Project setup
<h4 align="center">
A Website, A Visual Representation of Students' Grade Data on Veracross
</h4>
<h5 align="center">
<a href="#intro">Introduction</a>&nbsp;&nbsp;
<a href="#setup">Project Setup</a>&nbsp;&nbsp;
<a href="#license">License</a>
</h5><br><br><br>
<a name="intro"></a>
Introduction:
--------
This is a website that generates visual representation of students' grade data on Veracross. Currently there is only one graph and one numerical data representing the GPA. But also it just released yesterday! (Yay!) What do you expect this soon lol?
**Here's how it looks like right now:** *(Now all of you know my grades ;-;)*
![](https://user-images.githubusercontent.com/22280294/65841599-155ead00-e2f2-11e9-9d9f-c2f23c45d9a4.png)
<br>
<a name="setup"></a>
Project Setup:
--------
TODO: Actually write a project setup tutorial that's not generated by Vue on initialization ;-;.
### Install
```
npm install
```
@@ -15,15 +47,10 @@ npm run serve
npm run build
```
### Run your tests
```
npm run test
```
<br>
### Lints and fixes files
```
npm run lint
```
<a name="license"></a>
License: [MIT](https://choosealicense.com/licenses/mit/)
--------
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
The MIT license basically means that this project is open-soucred and you can do whatever you want with it, as long as you include a copy of this license in your distribution. You don't have to ask for permissions to use or anything. However, if you do bad things with it, I'm not responsible.
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# abort on errors
set -e
# build
npm run build
# navigate into the build output directory
cd dist
# if you are deploying to a custom domain
echo 'vera.hydev.org' > CNAME
git init
git add -A
git commit -m 'deploy'
# if you are deploying to https://<USERNAME>.github.io/<REPO>
git push -f git@github.com:HyDevelop/VeracrossAnalyzer.Client.git master:gh-pages
cd -
+27 -3
View File
@@ -6,6 +6,7 @@ import Constants from '@/constants';
import JsonUtils from '@/utils/json-utils';
import pWaitFor from 'p-wait-for';
import {HttpUtils} from '@/utils/http-utils';
import {CourseUtils} from '@/utils/course-utils';
/**
* Objects of this interface represent assignment grades.
@@ -40,6 +41,12 @@ export interface Course
level: string,
scaleUp: number,
grading:
{
method: string,
weightingMap: {[index: string]: number}
}
assignments: Grade[]
}
@@ -54,8 +61,11 @@ export default class App extends Vue
// List of course that the student takes
public courses: Course[] = [];
// List of course that should be displayed
public filteredCourses: Course[] = [];
// Currently selected tab
public selectedTab: string = "overall";
public selectedTab: string = 'overall';
// Are the course assignments loaded from the server.
public assignmentsReady: boolean = false;
@@ -122,8 +132,6 @@ export default class App extends Vue
/**
* Load the assignments of the courses
*
* @param courses Courses Json
*/
public loadAssignments()
{
@@ -153,7 +161,11 @@ export default class App extends Vue
pWaitFor(() => this.isAssignmentsReady()).then(() =>
{
// When the assignments are ready
// TODO: Display loading
this.assignmentsReady = true;
// Filter courses
this.filteredCourses = CourseUtils.getGradedCourses(this.courses);
});
}
@@ -185,4 +197,16 @@ export default class App extends Vue
// Update selected tab
this.selectedTab = tab;
}
/**
* Sign out
*/
public signOut()
{
// Clear all cookies
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
// Refresh
window.location.reload();
}
}
+7 -2
View File
@@ -1,10 +1,15 @@
<template>
<div id="app">
<login v-if="showLogin" v-on:login:token="onLogin" :http="http"></login>
<navigation :courses="courses" v-on:navigation:select="onNavigate"></navigation>
<navigation :courses="filteredCourses"
v-on:sign-out="signOut()"
v-on:navigation:select="onNavigate">
</navigation>
<div id="app-content">
<overall :courses="courses" v-if="selectedTab === 'overall' && assignmentsReady"></overall>
<overall :courses="filteredCourses"
v-if="selectedTab === 'overall' && assignmentsReady">
</overall>
</div>
</div>
</template>
+9
View File
@@ -4,6 +4,7 @@ import {HttpUtils} from '@/utils/http-utils';
/**
* This component handles user login, and obtains data from the server.
* TODO: Press enter to login
*/
@Component({
components: {},
@@ -70,4 +71,12 @@ export default class Login extends Vue
this.loading = false;
});
}
/**
* This is called when the user hits enter in the input boxes.
*/
public onEnter()
{
this.onLoginClick();
}
}
+4 -2
View File
@@ -8,13 +8,15 @@
<el-input v-model="username"
placeholder="School Username"
:class="{'input-error': error !== ''}">
:class="{'input-error': error !== ''}"
@keyup.enter.native="onEnter">
</el-input>
<el-input v-model="password"
placeholder="Veracross Password"
show-password=""
:class="{'input-error': error !== ''}">
:class="{'input-error': error !== ''}"
@keyup.enter.native="onEnter">
</el-input>
<div class="el-form-item__error custom">{{error}}</div>
+9
View File
@@ -27,4 +27,13 @@ export default class Navigation extends Vue
// Call custom event
this.$emit('navigation:select', this.activeIndex);
}
/**
* This function is called when the sign out button is clicked.
*/
public signOut()
{
// Call custom event
this.$emit('sign-out');
}
}
+1
View File
@@ -11,6 +11,7 @@
:key="course.name">{{course.name}}</el-menu-item>
</el-submenu>
<el-button @click="signOut" id="sign-out-button" type="text">Sign Out</el-button>
</el-menu>
<div class="line"></div>
</div>
+27 -2
View File
@@ -5,7 +5,6 @@ export default class Constants
{
/**
* Base url for api access
* TODO: Use https for actual usage
*/
public static API_URL: string = 'https://va.hydev.org/api';
@@ -14,5 +13,31 @@ export default class Constants
'| |. , |---|,---.,---.| , .,---,,---.,---.\n' +
' \\ / >< | || |,---|| | | .-\' |---\'| \n' +
' `\' \' ` ` \'` \'`---^`---\'`---|\'---\'`---\'` \n' +
' v1.1.0 `---\' '
' v0.2.2.303 `---\' ';
// Graph Theme
public static THEME =
{
// Colors
colors:
[
'#18cea5',
'#4fa8ed',
'#f9627b',
'#ffb075',
'#005c9c',
'#bcabe0',
'#d36e75',
'#fc97af',
'#919e8b',
'#d7ab82',
'#6e7074',
'#61a0a8',
'#efa18d',
'#787464',
'#cc7e63',
'#724e58',
'#4b565b'
]
}
}
@@ -0,0 +1,7 @@
#graph-average
{
.graph
{
margin-top: 50px;
}
}
@@ -0,0 +1,112 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import {Course} from '@/components/app/app';
import {GPAUtils} from '@/utils/gpa-utils';
import Constants from '@/constants';
@Component({
})
export default class GraphAverage extends Vue
{
// @ts-ignore
@Prop({required: true}) courses: Course[];
/**
* Generate settings
*/
get chartSettings()
{
let settings =
{
// Title
title:
{
show: true,
textStyle:
{
fontSize: 12
},
text: 'Course GPA',
subtext: 'Current GPA for every course',
x: 'center'
},
// X axis represents course names
xAxis:
{
type: 'category',
axisLabel: {
interval: 0,
inside: false,
rotate: 90,
// Truncate text length
formatter: (value: string) => value.length <= 16 ? value : value.substr(0, 14) + '...'
},
},
// Y axis represents GPAs and MaxGPAs
yAxis:
{
type: 'value'
},
// Data
series:
[
{
type: 'bar',
barGap: '-100%',
data: this.courses.map(course =>
{
return {value: [course.name, GPAUtils.getGP(course, 'A+')],
itemStyle: {color: '#d8d8d8'}}
}),
},
{
type: 'bar',
barGap: '-100%',
data: this.generateGPData(),
label:
{
show: true,
rotate: 90
}
}
],
// Disable tooltip
tooltip:
{
show: false
}
};
// TODO: Remove this
console.log(settings);
return settings;
}
/**
* Generate GP data for each course
*/
private generateGPData()
{
let data: any = [];
this.courses.forEach(course =>
{
data.push(
{
value: [course.name, GPAUtils.getGP(course, course.letterGrade)],
itemStyle:
{
color: Constants.THEME.colors[data.length]
}
});
});
return data;
}
}
@@ -0,0 +1,9 @@
<template>
<div id="graph-average">
<ve-bar height="450px" class="graph"
:extend="chartSettings"></ve-bar>
</div>
</template>
<script src="./graph-average.ts" lang="ts"></script>
<style src="./graph-average.scss" lang="scss"></style>
+159 -11
View File
@@ -5,18 +5,166 @@ import {Course} from '@/components/app/app';
})
export default class GraphOverall extends Vue
{
@Prop({required: true}) chart: any;
// @ts-ignore
@Prop({required: true}) courses: Course[];
public chartData =
private settings =
{
columns: ['日期', '访问用户', '下单用户', '下单率'],
rows: [
{ '日期': '1/1', '访问用户': 1393, '下单用户': 1093, '下单率': 0.32 },
{ '日期': '1/2', '访问用户': 3530, '下单用户': 3230, '下单率': 0.26 },
{ '日期': '1/3', '访问用户': 2923, '下单用户': 2623, '下单率': 0.76 },
{ '日期': '1/4', '访问用户': 1723, '下单用户': 1423, '下单率': 0.49 },
{ '日期': '1/5', '访问用户': 3792, '下单用户': 3492, '下单率': 0.323 },
{ '日期': '1/6', '访问用户': 4593, '下单用户': 4293, '下单率': 0.78 }
]
// Title
title:
{
show: true,
textStyle:
{
fontSize: 12
},
text: 'Average Grade',
subtext: 'Average score trend for every course',
x: 'center'
},
// Legend
legend:
{
show: false,
//left: 'auto',
//align: 'left',
//orient: 'vertical'
textStyle:
{
fontSize: 11
},
icon: 'circle'
},
// Zoom bar
dataZoom:
[
// TODO: Calculate real value for startValue
{
startValue: '9/13/2019'
},
{
type: 'inside'
}
],
series:
{
smooth: true
},
xAxis:
{
//type: 'time'
},
yAxis:
{
min: (value: any) => value.min - 10
}
};
/**
* Convert assignments list to a graph dataset.
*/
get convertChart()
{
let courses = this.courses;
// Compute the column names
let columns = courses.map(course => course.name);
columns.unshift('date');
// Find the min date
let minDates = courses.map(course => new Date(course.assignments[course.assignments.length - 1].date).getTime());
let minDate: Date = new Date(Math.min.apply(null, minDates));
// Find the dates in between
let now = new Date();
let dates = [];
for (let date = minDate; date <= now; date.setDate(date.getDate() + 1))
{
dates.push(new Date(date));
}
// 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 =>
{
// 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
let assignmentDate = new Date(assignment.date);
if (assignmentDate.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
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;
}
});
// Add it to the array
rows.push(row);
});
console.log(rows);
return {
columns: columns,
rows: rows
}
}
}
@@ -1,6 +1,6 @@
<template>
<div id="graph-overall">
<ve-line :data="chart" :extend="{series: {smooth: false}}"></ve-line>
<ve-line :data="convertChart" :extend="settings"></ve-line>
</div>
</template>
+37 -4
View File
@@ -12,18 +12,51 @@
flex-direction: column;
}
.span-gpa-header
.gpa-card
{
margin-left: 20px;
min-width: 136px;
}
.gpa
{
display: block;
}
.gpa.header
{
font-size: 14px;
}
.span-gpa
.gpa.text
{
font-size: 35px;
font-family: 'Avenir', Helvetica, Arial, sans-serif;
}
.gpa-time
.gpa.max
{
font-size: 14px;
margin-top: -10px;
margin-bottom: 10px;
font-size: 12px;
color: #409eff;
}
.gpa.time
{
font-size: 11px;
}
// Graph average
.graph-average-card
{
margin-right: 20px;
min-width: 170px;
}
// Fix padding
.el-card__body
{
padding-top: 0 !important;
padding-bottom: 0 !important;
}
+9 -112
View File
@@ -1,126 +1,23 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import GraphOverall from '@/pages/overall/graph-overall/graph-overall';
import GraphAverage from '@/pages/overall/graph-average/graph-average';
import {Course} from '@/components/app/app';
import {GPAUtils} from '@/utils/gpa-utils';
@Component({
components: {GraphOverall}
components: {GraphOverall, GraphAverage}
})
export default class Overall extends Vue
{
@Prop({required: true}) courses: any;
get convertCharts()
{
// Null case
if (this.courses == null) return [];
// Compute the column names
let columns = ['date'];
this.courses.forEach((course: Course) =>
{
// Ignore non-important courses
if (course.status != 'active') return;
columns.push(course.name);
});
// Find the min date
let minDate: Date = new Date();
this.courses.forEach((course: Course) =>
{
// Ignore non-important courses
if (course.status != 'active') return;
if (course.assignments.length == 0) return;
let date = new Date(course.assignments[course.assignments.length - 1].date);
if (date < minDate) minDate = date;
});
// Find the dates in between
let now = new Date();
let dates = [];
for (let date = minDate; date <= now; date.setDate(date.getDate() + 1))
{
dates.push(new Date(date));
}
// Initialize course specific variables
let courseScores: {[index: string]: any} = {};
let courseMaxScores: {[index: string]: any} = {};
let courseIndexes: {[index: string]: any} = {};
this.courses.forEach((course: Course) =>
{
// Ignore non-important courses
if (course.status != 'active') return;
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
this.courses.forEach((course: Course) =>
{
// Ignore non-important courses
if (course.status != 'active') return;
// Reversed loop through the assignments
for (let r = courseIndexes[course.name]; r >= 0; r--)
{
let assignment = course.assignments[r];
let assignmentDate = new Date(assignment.date);
// Date is being looked at
if (assignmentDate.getTime() == date.getTime())
{
// Record scores
courseScores[course.name] += assignment.score;
courseMaxScores[course.name] += assignment.scoreMax;
}
// 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);
});
console.log(rows);
return {
columns: columns,
rows: rows
}
}
// @ts-ignore
@Prop({required: true}) courses: Course[];
/**
* This function is called to get gpa since I can't import another
* class in the Vue file.
*/
public getGPA()
{
let gpa = GPAUtils.getGPA(this.courses);
let result = '' + gpa.gpa;
/* Not accurate
if (!gpa.accurate)
{
result = `(${result})`;
}*/
return result;
return GPAUtils.getGPA(this.courses);
}
}
+14 -12
View File
@@ -2,23 +2,25 @@
<div id="overall">
<el-row>
<el-col :span="4">
<el-card style="margin-left: 20px">
<el-card class="gpa-card">
<div style="padding: 14px;">
<span class="span-gpa-header">GPA:</span>
<br>
<span class="span-gpa">{{getGPA()}}</span>
<div class="bottom clearfix gpa-time">
<time class="time">{{ new Date().toDateString() }}</time>
<br>
<el-button type="text" class="button">Button</el-button>
<span class="gpa header">GPA:</span>
<span class="gpa text">{{getGPA().gpa}}</span>
<span class="gpa max">(Out of {{getGPA().max}})</span>
<div class="bottom clearfix gpa time">
<time>{{ new Date().toDateString() }}</time>
</div>
</div>
</el-card>
</el-col>
<el-col :span="20">
<el-card style="margin-right: 20px">
<p>Your average score graph all time:</p>
<graph-overall :chart="convertCharts"></graph-overall>
<el-col :span="14">
<el-card>
<graph-overall :courses="courses"></graph-overall>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="graph-average-card">
<graph-average :courses="courses"></graph-average>
</el-card>
</el-col>
</el-row>
+37
View File
@@ -0,0 +1,37 @@
import {Course} from '@/components/app/app';
export class CourseUtils
{
/**
* Return a list of courses that are graphed
*
* @param original Original course list
* @return Course[] Filtered course list
*/
public static getGradedCourses(original: Course[]): Course[]
{
// Define result
let result: Course[] = [];
// Filter through courses
original.forEach(course =>
{
// Skip future or past courses
if (course.status != 'active') return;
// Skip courses without levels
if (course.level == 'None') return;
// Skip courses without assignments
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
result.push(course);
});
return result;
}
}
+100
View File
@@ -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;
}
+11 -7
View File
@@ -31,7 +31,7 @@ export class GPAUtils
*
* @param coursesOriginal List of courses
*/
public static getGPA(coursesOriginal: Course[]): {gpa: number, accurate: boolean}
public static getGPA(coursesOriginal: Course[]): {gpa: number, accurate: boolean, max: number}
{
// Clone array
let courses: Course[] = [];
@@ -42,7 +42,7 @@ export class GPAUtils
// Remove all courses that does not have a grade
coursesOriginal.forEach(course =>
{
if (course.numericGrade == null)
if (course.letterGrade == null || course.letterGrade == '')
{
accurate = false;
}
@@ -55,35 +55,39 @@ export class GPAUtils
// If no course have grade, return -1
if (courses.length == 0)
{
return {gpa: -1, accurate: false};
return {gpa: -1, accurate: false, max: -1};
}
// Count total GPA
let totalGPA = 0;
let maxTotal = 0;
courses.forEach(course =>
{
totalGPA += this.getGP(course);
totalGPA += this.getGP(course, course.letterGrade);
maxTotal += this.getGP(course, 'A+');
});
// Get average GPA, round to two decimal places
let gpa = Math.round(totalGPA / courses.length * 100) / 100;
let maxGPA = Math.round(maxTotal / courses.length * 100) / 100;
// Return results
return {gpa: gpa, accurate: accurate};
return {gpa: gpa, accurate: accurate, max: maxGPA};
}
/**
* Calculate GPA for a course
*
* @param course Course
* @param letterGrade Letter grade
*/
public static getGP(course: Course): number
public static getGP(course: Course, letterGrade?: string): number
{
// Find the GPA for this course.
for (let scale of this.SCALE)
{
// Letter grades are the same
if (scale[this.LETTER] == course.letterGrade)
if (scale[this.LETTER] == letterGrade)
{
// Get grade and add it
let grade = <number> scale[this.GPA];
+1 -1
View File
@@ -19,7 +19,7 @@ export default class JsonUtils
type: assignment.assignment_type,
description: assignment.assignment_description,
date: assignment._date,
complete: assignment.complete_status,
complete: assignment.completion_status,
include: assignment.include_in_calculated_grade == 1,
display: assignment.display_grade == 1,
+5 -3
View File
@@ -1,5 +1,7 @@
module.exports = {
devServer: {
module.exports =
{
devServer:
{
disableHostCheck: true,
}
}
};