Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0025ec9213 | |||
| 489328c624 | |||
| 36a211e186 | |||
| d7c4c87959 | |||
| 3cd9db65ec | |||
| e03798af13 | |||
| 1fd4e89f84 | |||
| c05b799334 | |||
| b9f0316f76 | |||
| 2b3cbb4061 | |||
| 0653deba64 | |||
| 67a9417f2b | |||
| ca49223432 | |||
| 6d0d23d0dd | |||
| 6c9061d4d9 | |||
| 56b9be01ba | |||
| c8935c41cb | |||
| 1d96dcc0a8 | |||
| 811de8f5e2 | |||
| ec4196088a | |||
| 863fdcb50e |
Generated
+17
-2
@@ -7546,8 +7546,7 @@
|
|||||||
"p-finally": {
|
"p-finally": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/p-finally/download/p-finally-1.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/p-finally/download/p-finally-1.0.0.tgz",
|
||||||
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
|
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"p-is-promise": {
|
"p-is-promise": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@@ -7588,12 +7587,28 @@
|
|||||||
"retry": "^0.12.0"
|
"retry": "^0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"p-timeout": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-C27DYI+tCroT8J8cTEyySGydl2B7FlxrGNF5/wmMbl1V+jeehUCzEE/BVgzRebdm2K3ZitKOKx8YbdFumDyYmw==",
|
||||||
|
"requires": {
|
||||||
|
"p-finally": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"p-try": {
|
"p-try": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz",
|
"resolved": "https://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz",
|
||||||
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
|
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"p-wait-for": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-0Uy19uhxbssHelu9ynDMcON6BmMk6pH8551CvxROhiz3Vx+yC4RqxjyIDk2V4ll0g9177RKT++PK4zcV58uJ7A==",
|
||||||
|
"requires": {
|
||||||
|
"p-timeout": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pako": {
|
"pako": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npm.taobao.org/pako/download/pako-1.0.10.tgz",
|
"resolved": "https://registry.npm.taobao.org/pako/download/pako-1.0.10.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"core-js": "^2.6.5",
|
"core-js": "^2.6.5",
|
||||||
"echarts": "^4.2.1",
|
"echarts": "^4.2.1",
|
||||||
"element-ui": "^2.11.1",
|
"element-ui": "^2.11.1",
|
||||||
|
"p-wait-for": "^3.1.0",
|
||||||
"v-charts": "^1.19.0",
|
"v-charts": "^1.19.0",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-class-component": "^7.0.2",
|
"vue-class-component": "^7.0.2",
|
||||||
|
|||||||
@@ -2,24 +2,62 @@ import {Component, Vue} from 'vue-property-decorator';
|
|||||||
import Login from '@/components/login/login';
|
import Login from '@/components/login/login';
|
||||||
import Navigation from '@/components/navigation/navigation';
|
import Navigation from '@/components/navigation/navigation';
|
||||||
import Overall from '@/pages/overall/overall';
|
import Overall from '@/pages/overall/overall';
|
||||||
|
import Constants from '@/constants';
|
||||||
|
import JsonUtils from '@/utils/json-utils';
|
||||||
|
import pWaitFor from 'p-wait-for';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objects of this interface represent assignment grades.
|
||||||
|
*/
|
||||||
|
export interface Grade
|
||||||
|
{
|
||||||
|
type: string,
|
||||||
|
description: string,
|
||||||
|
date: string,
|
||||||
|
complete: string,
|
||||||
|
include: boolean,
|
||||||
|
display: boolean,
|
||||||
|
|
||||||
|
scoreMax: number,
|
||||||
|
score: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A course
|
||||||
|
*/
|
||||||
|
export interface Course
|
||||||
|
{
|
||||||
|
assignmentsId: number,
|
||||||
|
id: number,
|
||||||
|
name: string,
|
||||||
|
teacherName: string,
|
||||||
|
|
||||||
|
assignments: Grade[]
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {Login, Navigation, Overall},
|
components: {Login, Navigation, Overall},
|
||||||
})
|
})
|
||||||
export default class App extends Vue
|
export default class App extends Vue
|
||||||
{
|
{
|
||||||
|
// Is the login panel shown
|
||||||
public showLogin: boolean = true;
|
public showLogin: boolean = true;
|
||||||
|
|
||||||
public courses = null;
|
// List of course that the student takes
|
||||||
|
public courses: Course[] = [];
|
||||||
|
|
||||||
|
// Currently selected tab
|
||||||
public selectedTab: string = "overall";
|
public selectedTab: string = "overall";
|
||||||
|
|
||||||
|
// Are the course assignments loaded from the server.
|
||||||
|
public assignmentsReady: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the user logs in.
|
* This is called when the user logs in.
|
||||||
*
|
*
|
||||||
* @param courses Courses Json
|
* @param courses Courses Json
|
||||||
*/
|
*/
|
||||||
public onLogin(courses: any)
|
public onLogin(courses: Course[])
|
||||||
{
|
{
|
||||||
// Hide login bar
|
// Hide login bar
|
||||||
this.showLogin = false;
|
this.showLogin = false;
|
||||||
@@ -29,6 +67,47 @@ export default class App extends Vue
|
|||||||
|
|
||||||
// Debug output TODO: Remove this
|
// Debug output TODO: Remove this
|
||||||
console.log(courses);
|
console.log(courses);
|
||||||
|
|
||||||
|
// Get assignments for all the courses
|
||||||
|
this.courses.forEach(course =>
|
||||||
|
{
|
||||||
|
// Send request to get assignments
|
||||||
|
fetch(`${Constants.API_URL}/veracross/assignments?id=${course.assignmentsId}`).then(res =>
|
||||||
|
{
|
||||||
|
// Get response body text
|
||||||
|
res.text().then(text =>
|
||||||
|
{
|
||||||
|
// Parse json and filter it
|
||||||
|
course.assignments = JsonUtils.filterAssignments(JSON.parse(text));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
{
|
||||||
|
alert(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for assignments to be ready.
|
||||||
|
pWaitFor(() => this.isAssignmentsReady()).then(() =>
|
||||||
|
{
|
||||||
|
// When the assignments are ready
|
||||||
|
this.assignmentsReady = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are assignments ready or not
|
||||||
|
*
|
||||||
|
* @returns boolean Ready or not
|
||||||
|
*/
|
||||||
|
private isAssignmentsReady(): boolean
|
||||||
|
{
|
||||||
|
for (const course of this.courses)
|
||||||
|
{
|
||||||
|
if (course.assignments == null) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<navigation :courses="courses" v-on:navigation:select="onNavigate"></navigation>
|
<navigation :courses="courses" v-on:navigation:select="onNavigate"></navigation>
|
||||||
|
|
||||||
<div id="app-content">
|
<div id="app-content">
|
||||||
<overall v-if="selectedTab === 'overall'"></overall>
|
<overall :courses="courses" v-if="selectedTab === 'overall' && assignmentsReady"></overall>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import {Component, Vue} from 'vue-property-decorator';
|
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||||
|
import {Course} from '@/components/app/app';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
})
|
})
|
||||||
export default class GraphOverall extends Vue
|
export default class GraphOverall extends Vue
|
||||||
{
|
{
|
||||||
|
@Prop({required: true}) chart: any;
|
||||||
|
|
||||||
public chartData =
|
public chartData =
|
||||||
{
|
{
|
||||||
columns: ['日期', '访问用户', '下单用户', '下单率'],
|
columns: ['日期', '访问用户', '下单用户', '下单率'],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="graph-overall">
|
<div id="graph-overall">
|
||||||
<ve-line :data="chartData" :extend="{series: {smooth: false}}"></ve-line>
|
<ve-line :data="chart" :extend="{series: {smooth: false}}"></ve-line>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,98 @@
|
|||||||
import {Component, Vue} from 'vue-property-decorator';
|
import {Component, Prop, Vue} from 'vue-property-decorator';
|
||||||
import GraphOverall from '@/pages/overall/graph-overall/graph-overall';
|
import GraphOverall from '@/pages/overall/graph-overall/graph-overall';
|
||||||
|
import {Course} from '@/components/app/app';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {GraphOverall}
|
components: {GraphOverall}
|
||||||
})
|
})
|
||||||
export default class Overall extends Vue
|
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) =>
|
||||||
|
{
|
||||||
|
columns.push(course.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find the min date
|
||||||
|
let minDate: Date = new Date();
|
||||||
|
this.courses.forEach((course: Course) =>
|
||||||
|
{
|
||||||
|
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) =>
|
||||||
|
{
|
||||||
|
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) =>
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="overall">
|
<div id="overall">
|
||||||
<p>这是 Overall</p>
|
<p>这是 Overall</p>
|
||||||
<graph-overall></graph-overall>
|
<graph-overall :chart="convertCharts"></graph-overall>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import {Grade} from '@/components/app/app';
|
||||||
|
|
||||||
|
export default class JsonUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method filters the information provided in an assignments json.
|
||||||
|
*
|
||||||
|
* @param assignments Assignments object
|
||||||
|
* @returns Grade[] Filtered assignment grade object list
|
||||||
|
*/
|
||||||
|
public static filterAssignments(assignments: any): Grade[]
|
||||||
|
{
|
||||||
|
let result: Grade[] = [];
|
||||||
|
|
||||||
|
assignments.assignments.forEach((assignment: any) =>
|
||||||
|
{
|
||||||
|
result.push(
|
||||||
|
{
|
||||||
|
type: assignment.assignment_type,
|
||||||
|
description: assignment.assignment_description,
|
||||||
|
date: assignment._date,
|
||||||
|
complete: assignment.complete_status,
|
||||||
|
include: assignment.include_in_calculated_grade == 1,
|
||||||
|
display: assignment.display_grade == 1,
|
||||||
|
|
||||||
|
scoreMax: assignment.maximum_score,
|
||||||
|
score: +assignment.raw_score
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user