Compare commits

...

730 Commits

Author SHA1 Message Date
Hykilpikonna 8dbef09ec9 [U] Release v0.4.2.912 2019-11-07 22:29:01 -05:00
Hykilpikonna 73b71f56a5 [S] Disable text selecting 2019-11-07 22:26:43 -05:00
Hykilpikonna ce702405d0 [S] Adjust no-grade font size 2019-11-07 22:26:33 -05:00
Hykilpikonna 21744a1bef [F] Fix gpa -1 when no grade issue 2019-11-07 22:26:13 -05:00
Hykilpikonna 82064f2f02 [O] Optimize graph max value 2019-11-07 22:14:46 -05:00
Hykilpikonna adfebc8c44 [F] Fix term 1 displaying term 2 grades issue 2019-11-07 22:00:08 -05:00
Hykilpikonna 2f30e67671 [F] Fix course grade displayed as 0.00 issue 2019-11-07 21:53:16 -05:00
Hykilpikonna 0a80d534eb [O] Optimize imports 2019-11-07 21:48:40 -05:00
Hykilpikonna a77c495843 [S] Further divide marklines 2019-11-07 21:45:34 -05:00
Hykilpikonna 5503aff6b1 [S] Dim marklines a little bit 2019-11-07 21:40:16 -05:00
Hykilpikonna 7cc4567245 [O] Optimize color representation 2019-11-07 21:39:09 -05:00
Hykilpikonna e7f29ad0bf [+] Add mark area below 70 2019-11-07 21:35:45 -05:00
Hykilpikonna cdbd101428 [+] Add mark area from 70 to 80 2019-11-07 21:33:22 -05:00
Hykilpikonna da650ef16b [+] Add mark area from 100 to 80 2019-11-07 21:31:51 -05:00
Hykilpikonna 8abfdd7f8e [+] Add mark area above 100 2019-11-07 21:31:36 -05:00
Hykilpikonna 3c66f99363 [F] Limit graph max value to 120 2019-11-07 20:57:35 -05:00
Hykilpikonna 251f87a072 [F] Fix assignment type update problem 2019-11-06 23:09:32 -05:00
Hykilpikonna e15c95561c [U] Release v0.4.1.895 2019-11-06 22:34:08 -05:00
Hykilpikonna f4ed39401c [S] Emphesize the title more 2019-11-06 22:29:03 -05:00
Hykilpikonna c3fb998254 [S] Set font size 2019-11-06 22:25:03 -05:00
Hykilpikonna d9e9bcc731 [S] Align the average span to the left 2019-11-06 22:24:56 -05:00
Hykilpikonna 110ff6daec [S] Center the average span 2019-11-06 22:24:46 -05:00
Hykilpikonna 7d83344e73 [F] Calculate percent average instead of ratio 2019-11-06 22:19:29 -05:00
Hykilpikonna 06d93398fb [S] Disable unread in courses page 2019-11-06 22:18:44 -05:00
Hykilpikonna b05328888e [M] Rename unread-entry class to assignment-entry 2019-11-06 22:17:25 -05:00
Hykilpikonna 19598f4a10 [+] Add average span 2019-11-06 22:16:59 -05:00
Hykilpikonna cd7102a5df [+] create method to get type average 2019-11-06 22:16:52 -05:00
Hykilpikonna 9bf1f6b624 [O] Optimize assignments filtering 2019-11-06 21:54:41 -05:00
Hykilpikonna f1681ad405 [F] Fix 'selectedTime undefined' 2019-11-06 21:54:11 -05:00
Hykilpikonna e0a70a23b0 [F] Fix: ' Invalid handler for event "command": got undefined' 2019-11-06 21:53:40 -05:00
Hykilpikonna 677dfb491a [+] Parse grading period 2019-11-06 21:50:28 -05:00
Hykilpikonna 07a9b38c8d [+] Add grading period field to app.ts 2019-11-06 21:50:22 -05:00
Hykilpikonna 53b78c4a94 [M] Rename cookie name too 2019-11-06 21:50:06 -05:00
Hykilpikonna 2057f08e40 [M] Rename time selection to grading period 2019-11-06 21:49:17 -05:00
Hykilpikonna 7bc843aa9a [O] Optimize assignments loading with [].map() 2019-11-06 21:41:49 -05:00
Hykilpikonna 0a8855e020 [F] Fix self-referencing error 2019-11-06 21:37:13 -05:00
Hykilpikonna 445d07c5e4 [+] Encapsulate method to get numeric and letter grades 2019-11-06 21:35:34 -05:00
Hykilpikonna 125e7d14de [O] Change letterGrade and numericGrade to raw fields 2019-11-06 21:32:55 -05:00
Hykilpikonna 4917f5cb0c [O] Optimize getGP 2019-11-06 21:32:38 -05:00
Hykilpikonna 2504b37eb7 [O] Also support numeric grade 2019-11-06 21:30:15 -05:00
Hykilpikonna b0d3bf4bd2 [+] Encapsulate method to find scale 2019-11-06 21:28:27 -05:00
Hykilpikonna e640384fd8 [O] Optimize scale list 2019-11-06 21:27:30 -05:00
Hykilpikonna adf7af6b84 [+] Create interface for scale 2019-11-06 21:27:19 -05:00
Hykilpikonna beb4155aab [+] Compare numeric grade with current term grade 2019-11-06 21:17:31 -05:00
Hykilpikonna 3337766ae6 [+] Add current term constant 2019-11-06 21:17:11 -05:00
Hykilpikonna dc135dc78b [+] Encapsulate getPercentTypeAverage 2019-11-06 21:16:47 -05:00
Hykilpikonna b39c51beea [O] getTotalMeanAverage pass in assignments not course 2019-11-06 21:16:31 -05:00
Hykilpikonna 971339e49c [F] Fix assignments order issue 2019-11-06 20:59:12 -05:00
Hykilpikonna 9db0431e96 [O] Use caching to optimize graphs 2019-11-06 20:57:52 -05:00
Hykilpikonna d667f2ab34 [-] Remove temp string 2019-11-06 20:39:58 -05:00
Hykilpikonna 9b895a8fd7 [+] Reload directly when selected time is changed 2019-11-06 20:39:47 -05:00
Hykilpikonna 66d7e18a71 [F] Fix selectedTime initialization issue 2019-11-06 20:30:05 -05:00
Hykilpikonna 4643155908 [+] Add useless string for updating 2019-11-06 20:29:39 -05:00
Hykilpikonna c6c3303a99 [O] Optimize onclick method call 2019-11-06 20:02:41 -05:00
Hykilpikonna 1aea558c40 [+] Store selected time in cookies 2019-11-06 20:02:29 -05:00
Hykilpikonna 316c7e1f63 [F] Fix 'cannot getTime() of undefined' 2019-11-06 20:01:32 -05:00
Hykilpikonna 8364befc91 [F] Fix more type problems 2019-11-06 19:26:41 -05:00
Hykilpikonna 47c25d0e71 [F] Fix imports 2019-11-06 19:23:39 -05:00
Hykilpikonna 422d574c6c [F] Fix term display number offset issue 2019-11-06 19:14:14 -05:00
Hykilpikonna 3400c07e77 [+] Encapsulate method to get assignments of specific time 2019-11-06 19:13:45 -05:00
Hykilpikonna 79bb10b14e [M] Rename assignments to rawAssignments 2019-11-06 19:12:24 -05:00
Hykilpikonna e7563fcfb5 [+] Encapsulate method to get selected time code 2019-11-06 19:11:55 -05:00
Hykilpikonna 43f0046827 [+] Update selected time dynamically 2019-11-06 19:11:41 -05:00
Hykilpikonna ef424dd9e3 [O] Unify time names 2019-11-06 19:11:27 -05:00
Hykilpikonna 37902b6d1f [+] Display selected time dynamically 2019-11-06 19:11:13 -05:00
Hykilpikonna 92e692f69a [O] Remove getGradedCourses 2019-11-06 18:27:19 -05:00
Hykilpikonna b6e0e12cab [+] Add isGraded method to course 2019-11-06 18:25:48 -05:00
Hykilpikonna 6eb7e421e0 [M] Move level detection to course 2019-11-06 18:22:35 -05:00
Hykilpikonna fbb9352546 [O] Move name parsing to course 2019-11-06 18:15:54 -05:00
Hykilpikonna 6f5c4f3a09 [M] Move loadAssignments to course 2019-11-06 18:13:24 -05:00
Hykilpikonna 1e91cec8d2 [O] Create course objects after parsing json 2019-11-06 18:08:47 -05:00
Hykilpikonna 17262e12af [+] Create constructor 2019-11-06 18:05:53 -05:00
Hykilpikonna 56a954c235 [O] Separate course object 2019-11-06 18:03:27 -05:00
Hykilpikonna da5eced769 [F] Fix pushed data duplicate issue 2019-11-06 17:55:10 -05:00
Hykilpikonna 71f2eccab4 [F] Fix filter assignment order issue 2019-11-06 17:54:57 -05:00
Hykilpikonna a5162c1f5b [O] Optimize date usage 2019-11-06 17:38:53 -05:00
Hykilpikonna 53c82fd477 [F] Fix sorting 2019-11-06 17:38:36 -05:00
Hykilpikonna 2867f8d09c [+] Set computed data 2019-11-06 17:26:52 -05:00
Hykilpikonna 5b902171c8 [+] Filter assignments into terms 2019-11-06 17:26:44 -05:00
Hykilpikonna c40c5b6b94 [+] Create computed model in course 2019-11-06 17:26:29 -05:00
Hykilpikonna b66c313b05 [+] Define terms date list 2019-11-06 17:20:09 -05:00
Hykilpikonna 48287cdc4b [O] Sort by date 2019-11-06 17:17:02 -05:00
Hykilpikonna 750c768848 [O] Use Date for date instead of string 2019-11-06 17:16:49 -05:00
Hykilpikonna 4b512d64d9 [+] Add commands to dropdown options 2019-11-06 16:48:43 -05:00
Hykilpikonna e121210e96 [S] Add proper up and down margins 2019-11-06 16:34:08 -05:00
Hykilpikonna 5a87608fa6 [S] Align dropdown to the right 2019-11-06 16:33:59 -05:00
Hykilpikonna e819abe789 [S] Make dropdown button smaller 2019-11-06 16:33:42 -05:00
Hykilpikonna eb718d14d1 [S] Add id to dropdown 2019-11-06 16:33:35 -05:00
Hykilpikonna 564896e940 [+] Create time selection button 2019-11-06 16:29:02 -05:00
Hykilpikonna d91c3875b0 [F] Fix bar graph color problem 2019-11-06 16:18:39 -05:00
Hykilpikonna 1fe2edb9f4 [F] Fix bar graph -1 problem 2019-11-06 16:17:31 -05:00
Hykilpikonna 91977e1226 [S] Change assignment type head font size 2019-11-06 16:06:33 -05:00
Hykilpikonna 948018c7de [F] Fix grade display when no grade exist 2019-11-06 16:06:03 -05:00
Hykilpikonna f2350680c8 [S] Optimize narrow height 2019-11-05 22:57:19 -05:00
Hykilpikonna 4cc424e079 [S] Make percentage score's width unified 2019-11-05 22:57:09 -05:00
Hykilpikonna 49d26fcf61 [S] Make month's width unified 2019-11-05 22:56:51 -05:00
Hykilpikonna 9fde3b21d4 [S] Fix margins when there are no unread 2019-11-05 22:51:47 -05:00
Hykilpikonna 6952649058 [S] Optimize date formatting 2019-11-05 22:51:15 -05:00
Hykilpikonna b12a717cea [S] Move unread property to css class 2019-11-05 22:41:33 -05:00
Hykilpikonna 8e1e222656 [S] Align the scores the same width 2019-11-05 22:39:57 -05:00
Hykilpikonna 8f7775bb69 [O] Reduce height for narrow layout 2019-11-05 22:33:22 -05:00
Hykilpikonna 0477161af2 [+] Create narrow property for class 2019-11-05 22:29:43 -05:00
Hykilpikonna 3b387db5b5 [S] Fix float left alignment issue 2019-11-05 22:24:45 -05:00
Hykilpikonna d85d0e4e36 [S] Align the text to the left 2019-11-05 22:23:25 -05:00
Hykilpikonna b74b086f98 [S] Vertically center the text 2019-11-05 22:23:14 -05:00
Hykilpikonna e1d20b822f [S] Optimize font size and color 2019-11-05 22:22:53 -05:00
Hykilpikonna e98997f230 [S] Specify background color 2019-11-05 18:25:56 -05:00
Hykilpikonna 82e17030d4 [+] Add property for background style 2019-11-05 18:22:44 -05:00
Hykilpikonna d1905ee2b1 [+] Create info div 2019-11-05 18:09:12 -05:00
Hykilpikonna 310b2bcee1 [S] Remove card body padding 2019-11-05 18:07:49 -05:00
Hykilpikonna d55eebce19 [F] Fix: Next course doesn't show up on mobile devices 2019-11-05 18:00:04 -05:00
Hykilpikonna 03507968ee [+] Add assignment entry components 2019-11-05 17:51:36 -05:00
Hykilpikonna 56769e4518 [F] Fix: Filter assignments based on value of type 2019-11-05 17:51:22 -05:00
Hykilpikonna fba589d6fe [+] Display type name 2019-11-05 17:44:16 -05:00
Hykilpikonna ff5b22b5dc [+] Filter assignment type 2019-11-05 17:42:46 -05:00
Hykilpikonna 14de2e0b23 [+] Create assignments property 2019-11-05 17:40:50 -05:00
Hykilpikonna 880e331c99 [F] Fix distinct key for v-for problem 2019-11-05 17:37:42 -05:00
Hykilpikonna cf794588df [-] Reomve duplicate type names 2019-11-05 17:37:29 -05:00
Hykilpikonna d362598d83 [+] Show an assignment type for each type 2019-11-05 17:29:27 -05:00
Hykilpikonna 536e98642d [+] Encapsulate method to get assignment types 2019-11-05 17:28:28 -05:00
Hykilpikonna baae0d088c [+] Add assignment head element in course page 2019-11-05 17:28:18 -05:00
Hykilpikonna 202feb12d7 [+] Create assignment type head vue comp 2019-11-05 17:22:13 -05:00
Hykilpikonna d2ab7c059f [F] Fix clear cookie issue 2019-11-04 16:23:40 -05:00
Hykilpikonna e1ef117dcd [S] Make logo non-clickable 2019-11-03 11:18:35 -05:00
Hykilpikonna fb3186d575 [S] Add shadow effect to logo 2019-11-03 11:18:26 -05:00
Hykilpikonna b913873951 [S] Adjust margin font size 2019-11-03 11:18:01 -05:00
Hykilpikonna bbdcb42316 [S] Adjust title margin size 2019-11-03 11:17:53 -05:00
Hykilpikonna 5438637224 [S] Add the title 2019-11-03 11:17:33 -05:00
Hykilpikonna 970a058ba3 [U] Pre-release v0.3.6.784 2019-11-03 10:44:57 -05:00
Hykilpikonna 5d295db1b7 [O] Add todo: update token each access 2019-11-03 10:43:50 -05:00
Hykilpikonna b22aac7ca2 [F] Fix cookie expiration issue 2019-11-03 10:26:10 -05:00
Hykilpikonna eef29a4611 [U] Add 'Unknown' to graded courses detection 2019-11-02 23:10:58 -04:00
Hykilpikonna 131952ed37 [O] Trim course names 2019-11-02 23:07:13 -04:00
Hykilpikonna 2e95c2550e [+] Create 404 redirection 2019-11-02 23:04:05 -04:00
Hykilpikonna 0db6e0d693 [S] Add some page end margins 2019-11-02 22:58:37 -04:00
Hykilpikonna 27198ad4e0 [U] Use CourseUtils.postProcess() 2019-11-02 22:57:41 -04:00
Hykilpikonna 3cbb6ebee6 [+] Add "HS" clubs to level detection 2019-11-02 22:56:27 -04:00
Hykilpikonna 2308df65b5 [+] Add post process for courses 2019-11-02 22:54:30 -04:00
Hykilpikonna c8f82cc991 [+] Use unknown course list 2019-11-02 22:54:02 -04:00
Hykilpikonna a6e1b905ed [O] Make unknown course list a map 2019-11-02 22:49:49 -04:00
Hykilpikonna 9432e9a806 [+] Create method to detect course level 2019-11-02 22:46:21 -04:00
Hykilpikonna d90280a10f [+] Create unknown course list 2019-11-02 22:46:04 -04:00
Hykilpikonna db30b7f807 [+] Create club constant 2019-11-02 22:45:52 -04:00
Hykilpikonna 20910b1562 [+] Add level constants 2019-11-02 22:18:58 -04:00
Hykilpikonna 5659a049e5 [O] Parse & in course names 2019-11-02 20:41:11 -04:00
Hykilpikonna 085812d859 [+] Create method to parse html text 2019-11-02 20:37:41 -04:00
Hykilpikonna e60a4669ac [F] Fix execution order problem 2019-11-02 20:21:26 -04:00
Hykilpikonna ac84907a98 [F] Update index only after loading 2019-11-02 20:20:09 -04:00
Hykilpikonna 04ee69e8e2 [F] Fix clickable required issue 2019-11-02 20:12:14 -04:00
Hykilpikonna a2445aca6a [F] Fix path reading issue 2019-11-02 20:11:39 -04:00
Hykilpikonna 336a58b23d [O] Move course head scss to separate file 2019-11-02 20:02:06 -04:00
Hykilpikonna 5a5cf9bd4d [O] Replace more ts-ignores with {required: true} 2019-11-02 20:00:19 -04:00
Hykilpikonna 44d262f457 [O] Inline course-page.ts 2019-11-02 19:58:25 -04:00
Hykilpikonna 27fda43373 [O] Inline overall.ts 2019-11-02 19:56:01 -04:00
Hykilpikonna b3de3b8405 [+] Only show mark as read if it is unread 2019-11-02 19:45:30 -04:00
Hykilpikonna 633918aa40 [U] Pass in unread value 2019-11-02 19:45:11 -04:00
Hykilpikonna 3bc59e87d3 [O] Inline assignment entry ts script 2019-11-02 19:41:55 -04:00
Hykilpikonna af7d9e9dca [+] Add unread prop to assignment entry 2019-11-02 19:40:26 -04:00
Hykilpikonna 3b8884dc84 [O] Optimize imports 2019-11-02 19:39:33 -04:00
Hykilpikonna 92158684c0 [-] Remove unnecessary ts-ignores 2019-11-02 19:38:45 -04:00
Hykilpikonna b37de9cf24 [O] Disable strict property initialization check 2019-11-02 19:37:13 -04:00
Hykilpikonna cd2dff5559 [M] Rename unread-entry to assignment-entry 2019-11-02 19:35:04 -04:00
Hykilpikonna d6a85af15c [O] Use relative date for starting value 2019-11-02 19:24:51 -04:00
Hykilpikonna 0d333879ca [O] Put login in a form 2019-11-02 19:08:02 -04:00
Hykilpikonna 905db3c73a [-] Remove debug logging 2019-11-02 19:04:56 -04:00
Hykilpikonna c5dfad8be8 [F] Remove trapezoid effect for mobile support 2019-11-02 19:03:35 -04:00
Hykilpikonna c9454a3832 [-] Remove todos that are done 2019-11-02 19:02:38 -04:00
Hykilpikonna c732d475f1 [O] Log when cookies doesn't exist 2019-10-23 20:20:54 -04:00
Hykilpikonna a4b7e0fd46 [O] Only clear cookies if cookies exists 2019-10-23 20:20:41 -04:00
Hykilpikonna bcee069b32 [F] Fix url index issue 2019-10-21 19:23:41 -04:00
Hykilpikonna 182208f8c3 [F] Fix Github Pages sub path issue by using # after url 2019-10-21 19:23:31 -04:00
Hykilpikonna 24befed17b [U] Pre-Release v0.3.6.741 2019-10-20 22:00:35 -04:00
Hykilpikonna 07991b2a0e [F] Update title after history 2019-10-20 21:59:11 -04:00
Hykilpikonna 51ea0c7a80 [+] Update title when updating history 2019-10-20 21:56:00 -04:00
Hykilpikonna 8b01428208 [+] Create function to find title 2019-10-20 21:55:39 -04:00
Hykilpikonna f69e2617d4 [+] Encapsulate toTitleCase() 2019-10-20 21:55:13 -04:00
Hykilpikonna ddfdb47b93 [+] Encapsulate findCourse 2019-10-20 21:49:51 -04:00
Hykilpikonna 14849f4211 [O] Move updateIndex logging to a better place 2019-10-20 21:42:44 -04:00
Hykilpikonna 067c599cb1 [F] Fix history pop state override issue 2019-10-20 21:42:18 -04:00
Hykilpikonna 33ceaa38c0 [O] Use Navigation.updateIndex instead 2019-10-20 21:39:08 -04:00
Hykilpikonna 0a0288c2ee [+] Make updateIndex public 2019-10-20 21:38:20 -04:00
Hykilpikonna 90f888bc4b [+] Add instance to navigation 2019-10-20 21:38:11 -04:00
Hykilpikonna 9bb34fb2a4 [O] Make course operations unselectable 2019-10-20 21:34:13 -04:00
Hykilpikonna 22b75a6b30 [+] Add .unselectable to global css 2019-10-20 21:33:43 -04:00
Hykilpikonna 6af8410698 [F] Fix logic mistake 2019-10-20 21:32:30 -04:00
Hykilpikonna f023c724fa [O] Don't update history for initial update 2019-10-20 21:31:22 -04:00
Hykilpikonna 6bbbe9cece [O] Encapsulate update history in updateIndex() 2019-10-20 21:31:05 -04:00
Hykilpikonna e6a4a04bb4 [+] Only show next course when next course isn't null 2019-10-20 21:27:42 -04:00
Hykilpikonna 5c4a391d96 [O] Add jsdocs for findNextCourse 2019-10-20 21:23:53 -04:00
Hykilpikonna a75b15d840 [O] Separate findNextCourse() and nextCourse() 2019-10-20 21:23:29 -04:00
Hykilpikonna 1322bd6326 [O] Adjust opacity 2019-10-20 21:21:47 -04:00
Hykilpikonna e2997c345c [F] Fix null case 2019-10-20 21:20:39 -04:00
Hykilpikonna 923a7e824f [O] Add drop shadows 2019-10-20 21:19:18 -04:00
Hykilpikonna 10ac0b5330 [O] Separate padding from common css 2019-10-20 21:16:10 -04:00
Hykilpikonna bfeba9da40 [O] Put prev-course under navbar 2019-10-20 21:15:56 -04:00
Hykilpikonna 752a865334 [O] Align prev-course upward 2019-10-20 21:14:36 -04:00
Hykilpikonna 5eda771070 [M] Move common css properties to common class 2019-10-20 21:13:51 -04:00
Hykilpikonna 0826080f82 [O] Add css class to both course operations 2019-10-20 21:13:15 -04:00
Hykilpikonna 6fab785a49 [+] Add previous course div 2019-10-20 21:11:03 -04:00
Hykilpikonna ecbcca5f14 [+] Add index offset to nextCourse() 2019-10-20 21:10:53 -04:00
Hykilpikonna 861de56f10 [O] Make cursor a pointer 2019-10-20 21:02:39 -04:00
Hykilpikonna 3ce66e1201 [+] Implement next course 2019-10-20 21:01:53 -04:00
Hykilpikonna 7e8ea73363 [O] Make it a trapezoid with rotateX 2019-10-20 20:56:51 -04:00
Hykilpikonna 9142525d21 [O] Adjust width 2019-10-20 20:56:13 -04:00
Hykilpikonna 62577ff1bb [O] Add padding 2019-10-20 20:56:06 -04:00
Hykilpikonna 953556ccee [O] Reduce font size 2019-10-20 20:51:47 -04:00
Hykilpikonna cc8621f304 [O] Add background 2019-10-20 20:51:41 -04:00
Hykilpikonna baae26d244 [O] Bottom align and center 2019-10-20 20:51:32 -04:00
Hykilpikonna cbf70cbeef [+] Create next course div in navbar 2019-10-20 20:48:18 -04:00
Hykilpikonna 1e28191e67 [U] Pre-Release v0.3.5.703 2019-10-20 20:27:46 -04:00
Hykilpikonna ef4d9d38e7 [O] Add '%' sign 2019-10-20 20:26:57 -04:00
Hykilpikonna 52626406bf [O] Use FormatUtils.limit 2019-10-20 20:26:18 -04:00
Hykilpikonna fec6c77dc2 [+] Create method to format string length 2019-10-20 20:23:47 -04:00
Hykilpikonna 4db8ee7837 [+] Add tooltip formatter 2019-10-20 20:20:58 -04:00
Hykilpikonna 9bfde0e39d [+] Add assignment description to the data 2019-10-20 20:20:45 -04:00
Hykilpikonna cfac8493cd [U] Pre-Release v0.3.5.697 2019-10-20 19:54:57 -04:00
Hykilpikonna 0d003a4f7e [O] Make new update clickable as well 2019-10-20 19:53:56 -04:00
Hykilpikonna 106a4759ae [O] Make clickable text cursor show as pointer 2019-10-20 19:53:42 -04:00
Hykilpikonna 06d09220c5 [O] Move clickable to global css 2019-10-20 19:46:19 -04:00
Hykilpikonna a19960b18f [O] Do nothing if not clickable 2019-10-20 19:44:06 -04:00
Hykilpikonna 656dd7d884 [+] Implement redirect on click in course-head 2019-10-20 19:42:06 -04:00
Hykilpikonna b965859537 [U] Use formatCourseIndex() in navigation 2019-10-20 19:41:50 -04:00
Hykilpikonna 707e96d2e9 [+] Encapsulate method to format course tab index 2019-10-20 19:41:28 -04:00
Hykilpikonna ab795674b2 [+] Add instance in App class 2019-10-20 19:36:38 -04:00
Hykilpikonna eb74feb659 [+] Create onClick function in course-head 2019-10-20 19:33:51 -04:00
Hykilpikonna 4b4c49b5c7 [+] Make courses in overall clickable 2019-10-20 19:29:52 -04:00
Hykilpikonna 1c134e02f9 [+] Add clickable prop to course-head 2019-10-20 19:29:18 -04:00
Hykilpikonna 85305f987f [O] Optimize course-head grid distribution 2019-10-20 19:23:12 -04:00
Hykilpikonna ec6e2b8c7e [F] Fix logical mistake 2019-10-20 19:22:55 -04:00
Hykilpikonna ab34435395 [U] Use VersionUtils.compare() in Login class 2019-10-20 19:17:21 -04:00
Hykilpikonna da96d846eb [+] Create function to compare version numbers 2019-10-20 19:11:22 -04:00
Hykilpikonna 9f6cb2658a [+] Create VersionUtils class 2019-10-20 19:00:47 -04:00
Hykilpikonna 33402012b6 [-] Remove deprecated file 2019-10-20 19:00:04 -04:00
Hykilpikonna 97cf1651f9 [+] Add MIN_SUPPORTED_VERSION 2019-10-20 18:59:26 -04:00
Hykilpikonna 97bfe69986 [O] Add logging before clearing cookies 2019-10-20 18:55:05 -04:00
Hykilpikonna b08fd7f433 [U] Use needToUpdateCookies() to check 2019-10-20 18:54:52 -04:00
Hykilpikonna 83c6a690ec [O] Optimize variable name lengths 2019-10-20 18:53:21 -04:00
Hykilpikonna cb54ae4dee [+] Create function to check version number 2019-10-20 18:53:03 -04:00
Hykilpikonna be2dcbf085 [O] Extend token saving period 2019-10-20 18:44:56 -04:00
Hykilpikonna 4df2726b95 [O] Make the chart full width (for now) 2019-10-20 18:40:35 -04:00
Hykilpikonna aa78e850bb [O] Adjust graph size 2019-10-20 18:39:50 -04:00
Hykilpikonna c05f18c12c [O] Add drop shadow 2019-10-20 18:38:16 -04:00
Hykilpikonna b4041a11a0 [O] Add cross tooltip pointer 2019-10-20 18:31:43 -04:00
Hykilpikonna bcf222a38b [O] Format x axis dates to MMM DD format 2019-10-20 18:28:07 -04:00
Hykilpikonna 7cfe928fcc [O] Adjust y label position 2019-10-20 18:25:12 -04:00
Hykilpikonna b5ad7c12e3 [O] Add '%' signs to y axis labels 2019-10-20 18:24:33 -04:00
Hykilpikonna 6286c5736e [O] Lighten legend label color 2019-10-20 18:20:35 -04:00
Hykilpikonna 0773d3ef0e [O] Make legend smaller 2019-10-20 18:20:21 -04:00
Hykilpikonna 7b912a9325 [O] Make title larger 2019-10-20 18:20:09 -04:00
Hykilpikonna 008155fa61 [O] Adjust legend icon width 2019-10-20 18:16:42 -04:00
Hykilpikonna 117cd9f568 [O] Adjust legend position 2019-10-20 18:16:15 -04:00
Hykilpikonna 3b14727f81 [O] Remove x axis name 2019-10-20 18:15:13 -04:00
Hykilpikonna 444a3733bd [O] Align legend to the bottom 2019-10-20 18:14:09 -04:00
Hykilpikonna de63ff9bb5 [+] Add legend 2019-10-20 18:13:56 -04:00
Hykilpikonna 2b2a4c59a8 [O] Adjust y-axis label position 2019-10-20 18:11:47 -04:00
Hykilpikonna 30f737bd7c [O] Center align y-axis label 2019-10-20 18:10:39 -04:00
Hykilpikonna 1e2c6ca66f [O] Make min x value of course-scatter a whole number 2019-10-20 18:04:43 -04:00
Hykilpikonna 7b13716ce3 [O] Make min x value of overall-line a whole number 2019-10-20 18:04:00 -04:00
Hykilpikonna 7a1b21a2ac [F] Fake course object usage to update computed value when tab switched 2019-10-20 17:36:12 -04:00
Hykilpikonna 2345bb6442 [F] Show only completed assignments 2019-10-20 17:30:00 -04:00
Hykilpikonna 47fd9e1db3 [O] Set max x value as today 2019-10-20 17:28:11 -04:00
Hykilpikonna f8eee081b3 [O] Adjust x axis label position 2019-10-20 17:26:28 -04:00
Hykilpikonna 84e0647c35 [O] Display x axis name 2019-10-20 17:23:22 -04:00
Hykilpikonna 60804dd98c [O] Add tooltip to scatter graph 2019-10-20 17:23:04 -04:00
Hykilpikonna 5789fac985 [-] Remove unnecessary :entend tag 2019-10-20 17:14:03 -04:00
Hykilpikonna 2a89c6316f [O] Override VE chart settings 2019-10-20 17:13:48 -04:00
Hykilpikonna 4c0a26a900 [-] Remove unused notification class 2019-10-20 17:02:22 -04:00
Hykilpikonna 6915617980 [+] Add inner top shadow to course card content 2019-10-20 17:01:58 -04:00
Hykilpikonna 0a09e14021 [-] Remove the todo 2019-10-20 16:58:48 -04:00
Hykilpikonna 34446494a9 [O] Round data 2019-10-20 13:06:30 -04:00
Hykilpikonna 8477c2f63b [O] Use percent score instead of 0 to 1 2019-10-20 13:00:52 -04:00
Hykilpikonna 105d1f7619 [+] Define colors 2019-10-20 12:59:32 -04:00
Hykilpikonna a4be712bd7 [F] Format date so that the chart can read it 2019-10-20 12:58:30 -04:00
Hykilpikonna 3a9a65920e [F] Use Array.from instead of map.forEach 2019-10-20 12:58:03 -04:00
Hykilpikonna ff62847758 [+] Debug with afterConfig() 2019-10-20 12:57:40 -04:00
Hykilpikonna 0207f617c2 [O] Optimize code format 2019-10-20 12:40:00 -04:00
Hykilpikonna 5c530952d3 [O] Add more colors 2019-10-20 12:39:46 -04:00
Hykilpikonna bdcefefd92 [O] Map assignments first 2019-10-20 12:12:25 -04:00
Hykilpikonna 68cebfa68c [O] Show tooltip 2019-10-20 12:12:00 -04:00
Hykilpikonna 94b337e772 [O] Change title of the chart 2019-10-20 12:06:39 -04:00
Hykilpikonna fd1f6223c0 [F] Fix date format 2019-10-20 12:06:29 -04:00
Hykilpikonna 0d86e461cd [+] Create function to format chart date 2019-10-20 12:03:01 -04:00
Hykilpikonna 9f06748b63 [+] Create format-utils 2019-10-20 11:59:41 -04:00
Hykilpikonna 9769fca6af [O] Label x and y axis 2019-10-20 11:57:37 -04:00
Hykilpikonna 416ef0991d [O] Make the x axis type time 2019-10-20 11:57:19 -04:00
Hykilpikonna 2150a563eb [F] Fix null case detection 2019-10-20 11:47:40 -04:00
Hykilpikonna 1ef08c17ec [F] Fix typo: 'course' instead of 'courses' 2019-10-20 11:46:34 -04:00
Hykilpikonna 15e375900c [+] Complete series 2019-10-20 11:45:42 -04:00
Hykilpikonna d70d54d3f7 [+] Create function to convert assignments to series data 2019-10-20 11:45:35 -04:00
Hykilpikonna 0159f639d2 [O] Optimize type with Map<string, Assignment[]> 2019-10-20 11:40:09 -04:00
Hykilpikonna 19d03536b5 [O] Specify return type 2019-10-20 11:36:35 -04:00
Hykilpikonna 214a716f16 [O] Clarify format in jsdocs 2019-10-20 11:35:11 -04:00
Hykilpikonna 1f5ecedf9f [+] Create function to map assignments 2019-10-20 11:34:48 -04:00
Hykilpikonna 3b52dab371 [+] Add course-scatter component to course-page 2019-10-20 09:53:01 -04:00
Hykilpikonna 40340a0abd [F] Fix "This dependency is not found" 2019-10-20 09:52:41 -04:00
Hykilpikonna c2311464f0 [+] Import course-scatter 2019-10-20 09:52:03 -04:00
Hykilpikonna f4f6fa2523 [+] Create course scatter vue component 2019-10-20 09:51:13 -04:00
Hykilpikonna 44d01eaec0 [O] Remove max height limit for course page 2019-10-20 09:48:30 -04:00
Hykilpikonna 300ff04f2e [F] Fix empty pathname detection with '/' 2019-10-14 22:14:46 -04:00
Hykilpikonna 6a34e9f706 [+] Add todo text 2019-10-14 22:13:22 -04:00
Hykilpikonna 97c8953dbd [-] Remove unnecessary overall-line.scss 2019-10-14 22:10:05 -04:00
Hykilpikonna 179d0bc567 [O] Make it scoped 2019-10-14 22:09:04 -04:00
Hykilpikonna 2d1242f3c0 [O] Inline overall-bar scss 2019-10-14 22:08:51 -04:00
Hykilpikonna b7f7e168b9 [O] Make css in unread-entry scoped 2019-10-14 22:07:47 -04:00
Hykilpikonna 83244ec496 [O] Make the css in overall-course and course-page scoped 2019-10-14 22:06:22 -04:00
Hykilpikonna eb3221fa4b [M] Move card padding to global css 2019-10-14 22:06:00 -04:00
Hykilpikonna 4e37ca4c44 [+] Add row on the expansion area 2019-10-14 21:36:57 -04:00
Hykilpikonna ae6172068e [+] Make course-page almost the same with overall-course 2019-10-14 21:35:55 -04:00
Hykilpikonna 65134c02af [+] Import course-head in course-page 2019-10-14 21:34:30 -04:00
Hykilpikonna fa9e1aae7c [O] Fix ' and " issue 2019-10-14 21:31:21 -04:00
Hykilpikonna 3d1401e1c2 [+] Add course-head element in overall-course 2019-10-14 20:33:25 -04:00
Hykilpikonna 8c102f5d5f [M] Move css to course-head 2019-10-14 20:33:05 -04:00
Hykilpikonna c4e1790444 [+] Import course-head in overall-course 2019-10-14 20:27:25 -04:00
Hykilpikonna 68c1fa1216 [M] Separate course-head from overall-course 2019-10-14 20:27:07 -04:00
Hykilpikonna eb094be9af [F] Move global css outside of overall scope 2019-10-14 20:26:13 -04:00
Hykilpikonna 8527e9860d [O] Make overall.scss scoped 2019-10-14 19:35:16 -04:00
Hykilpikonna ceb1ed1404 [O] Surround the course with a card 2019-10-14 19:34:12 -04:00
Hykilpikonna 968fa63f51 [O] Make course name font size 36px 2019-10-14 19:09:55 -04:00
Hykilpikonna 2d530d204e [O] Align text to the left 2019-10-14 19:08:21 -04:00
Hykilpikonna 65efde05ee [O] Add some margins 2019-10-14 19:08:12 -04:00
Hykilpikonna a5d261ac93 [+] Use grid to display basic info 2019-10-14 19:01:17 -04:00
Hykilpikonna b61cd3839a [O] Move v-if assignmentReady detection up an element 2019-10-14 18:45:01 -04:00
Hykilpikonna 665567bf88 [+] Bind the displayed course to the course page 2019-10-14 18:28:37 -04:00
Hykilpikonna a074a9fbed [+] Show course page only if select tab is a course 2019-10-14 18:27:37 -04:00
Hykilpikonna 0e30954f6e [+] Add course page 2019-10-14 18:27:09 -04:00
Hykilpikonna 0e98cffc64 [+] Update initial index 2019-10-14 18:26:26 -04:00
Hykilpikonna e7d5e766e3 [+] Add default path name when there are none 2019-10-14 18:02:02 -04:00
Hykilpikonna 88b0ef752c [+] Dynamically initialize first state based on pathname 2019-10-14 18:01:43 -04:00
Hykilpikonna 1a8fc9eab4 [-] Remove duplicate update 2019-10-14 17:57:56 -04:00
Hykilpikonna 76dd8f73e4 [O] Use updateIndex() instead 2019-10-14 17:57:43 -04:00
Hykilpikonna 4807c4babb [+] Encapsulate method to update index 2019-10-14 17:57:18 -04:00
Hykilpikonna 9b4f5291f0 [O] Use v-bind.sync to sync variable value 2019-10-14 17:57:04 -04:00
Hykilpikonna 5edc4c55f5 [-] Remove testing log 2019-10-14 16:43:21 -04:00
Hykilpikonna 563bf06b8e [-] Remove onNavigate event 2019-10-14 16:42:38 -04:00
Hykilpikonna cdaa2679d1 [M] Move pushState to navigation.ts as well 2019-10-14 16:42:09 -04:00
Hykilpikonna 4efac6023f [O] Bind activeIndex with selectedTab 2019-10-14 16:40:41 -04:00
Hykilpikonna c6e3806024 [M] Move history stuff to navigation.ts 2019-10-14 16:40:27 -04:00
Hykilpikonna e3df5b8e3b [+] Create history state listener 2019-10-14 16:35:00 -04:00
Hykilpikonna 5afc0aebd5 [+] Set state on start 2019-10-14 16:34:39 -04:00
Hykilpikonna d23446b409 [F] Push state after updating tab 2019-10-14 16:33:41 -04:00
Hykilpikonna bc63b457d3 [+] Clarify logging 2019-10-14 16:24:20 -04:00
Hykilpikonna 30a8d62402 [+] Keep last selected tabb 2019-10-14 16:23:16 -04:00
Hykilpikonna 349683de24 [F] Use absolute path for history 2019-10-14 09:40:18 -04:00
Hykilpikonna 06db7f48bd [F] Fix: can't replace all with replace() 2019-10-14 09:38:36 -04:00
Hykilpikonna 986a38d81d [O] Replace spaces in course name with '-' 2019-10-14 09:37:55 -04:00
Hykilpikonna 8b7ee75ca7 [+] Add course id to the navigation index 2019-10-14 09:36:37 -04:00
Hykilpikonna f59bed777b [O] Use all lowercase for course navigation 2019-10-14 09:36:02 -04:00
Hykilpikonna 87634f0df5 [+] Push history state on navigation 2019-10-14 09:32:19 -04:00
Hykilpikonna bb618e74c9 [+] Import course page in app 2019-10-14 09:31:54 -04:00
Hykilpikonna 92d2a7673b [O] Make text lighter 2019-10-13 22:25:48 -04:00
Hykilpikonna 232fe43eb5 [O] Add gradient to loading screen 2019-10-13 22:24:50 -04:00
Hykilpikonna d02cfe0557 [U] Release v0.3.4.561 2019-10-13 21:48:33 -04:00
Hykilpikonna aca0b5635b [+] Show error for assignments and grading too 2019-10-13 21:46:34 -04:00
Hykilpikonna 4e8d3aaeb9 [O] Fix line gap 2019-10-13 21:36:37 -04:00
Hykilpikonna 1c097b639a [O] Reverse dynamic font size 2019-10-13 21:36:28 -04:00
Hykilpikonna ed80b3c82b [O] Separate details and error details 2019-10-13 21:35:52 -04:00
Hykilpikonna 1a59e174ae [O] Make color class red (pink) 2019-10-13 21:31:21 -04:00
Hykilpikonna bbca7c2e14 [F] Should be class instead of style 2019-10-13 21:31:08 -04:00
Hykilpikonna 5c09490d5e [O] Dynamically add error class 2019-10-13 21:28:16 -04:00
Hykilpikonna ba9b24a276 [O] Encapsulate error message function 2019-10-13 21:28:04 -04:00
Hykilpikonna 755b61efe3 [O] Only show spinner if not error 2019-10-13 21:25:17 -04:00
Hykilpikonna 75d6d94757 [+] Switch title to 'Error' dynamically 2019-10-13 21:25:01 -04:00
Hykilpikonna a00285b38a [+] Bind loadingError variable 2019-10-13 21:23:32 -04:00
Hykilpikonna b529aa070a [O] Show error on http error too 2019-10-13 21:21:48 -04:00
Hykilpikonna 38d7b75831 [+] Encapsulate showError function 2019-10-13 21:20:45 -04:00
Hykilpikonna c3b81b5828 [+] Display error after course failed to load 2019-10-13 21:18:57 -04:00
Hykilpikonna 8aae753098 [+] Add loadingError field 2019-10-13 21:18:39 -04:00
Hykilpikonna 382a2aeabc [+] Add error property 2019-10-13 21:15:42 -04:00
Hykilpikonna ec056ef3b3 [+] Remove loading when it's done loading 2019-10-13 21:13:39 -04:00
Hykilpikonna 96d3e6b620 [O] Dynamically size entries based on index 2019-10-13 21:12:32 -04:00
Hykilpikonna 85f59a3983 [+] Add <br> every line 2019-10-13 21:07:50 -04:00
Hykilpikonna 71035ab87b [-] Remove unused pre css 2019-10-13 21:07:29 -04:00
Hykilpikonna bfd91af873 [O] Use span instead of pre 2019-10-13 21:07:17 -04:00
Hykilpikonna de5d406362 [O] Display loading line by line 2019-10-13 21:06:52 -04:00
Hykilpikonna 8c8f405aa5 [+] Add method to split loading messages 2019-10-13 21:03:58 -04:00
Hykilpikonna a59f2e6f7c [+] Log loading before checking grading 2019-10-13 21:02:46 -04:00
Hykilpikonna 5d06ee1c41 [+] Log loading before loading assignments 2019-10-13 21:02:38 -04:00
Hykilpikonna b818420df7 [+] Log loading before loading courses 2019-10-13 21:02:32 -04:00
Hykilpikonna dbe9c69771 [+] Log loading before login 2019-10-13 21:02:17 -04:00
Hykilpikonna 3a58699f62 [O] Fix <pre> tag font 2019-10-13 21:01:53 -04:00
Hykilpikonna f5804c3ce8 [O] Surround text with <pre> tag 2019-10-13 20:59:30 -04:00
Hykilpikonna 5ef7083ee6 [M] Move font-family to theme variable 2019-10-13 20:59:10 -04:00
Hykilpikonna 25370ec412 [+] Create function to log loading 2019-10-13 20:52:12 -04:00
Hykilpikonna b55c25aa7c [O] Hide loading on start because of the login screen 2019-10-13 20:48:17 -04:00
Hykilpikonna 9f68ac8377 [O] Only show loading when loading text is not empty 2019-10-13 20:46:46 -04:00
Hykilpikonna 335cde4d4e [+] Use actual loading text 2019-10-13 20:46:28 -04:00
Hykilpikonna 878b8cf87c [O] Inline loading script 2019-10-13 20:45:03 -04:00
Hykilpikonna 283904d976 [O] Align loading message to bottom 2019-10-13 20:43:52 -04:00
Hykilpikonna 11dea5182b [O] Make details color darker 2019-10-13 20:39:45 -04:00
Hykilpikonna 87f8f49c39 [O] Correct spinner alignments 2019-10-13 20:38:44 -04:00
Hykilpikonna 595033b80f [O] Make the stroke white 2019-10-13 20:37:21 -04:00
Hykilpikonna ccd32ed7e6 [+] Add a loading spinner 2019-10-13 20:35:53 -04:00
Hykilpikonna a40e3b24eb [O] Make details font size smaller 2019-10-13 20:35:41 -04:00
Hykilpikonna 6a4ff9b0a7 [O] Make title font size bigger 2019-10-13 20:35:27 -04:00
Hykilpikonna 2dfb0bf447 [+] Add loading title 2019-10-13 20:35:16 -04:00
Hykilpikonna 21e6352ace Revert "[-] Remove loading"
This reverts commit f1089dbdf5.
2019-10-13 20:29:45 -04:00
Hykilpikonna f1089dbdf5 [-] Remove loading 2019-10-13 20:29:02 -04:00
Hykilpikonna e95efbff59 [O] Center text box vertically 2019-10-13 20:00:10 -04:00
Hykilpikonna 92b46baeed [O] Center text horizontally 2019-10-13 19:59:59 -04:00
Hykilpikonna d45fce6506 [+] Add a loading component in html for testing 2019-10-13 19:43:30 -04:00
Hykilpikonna e4374a6731 [+] Add loading to app 2019-10-13 19:43:11 -04:00
Hykilpikonna cd402c4b86 [O] Make text color white 2019-10-13 19:42:56 -04:00
Hykilpikonna 25bf445582 [O] Darken loading background 2019-10-13 19:42:47 -04:00
Hykilpikonna 74287289e0 [O] Center loading screen 2019-10-13 19:42:33 -04:00
Hykilpikonna cc23f8565a [+] Create loading overlay 2019-10-13 19:42:17 -04:00
Hykilpikonna a537ced0d7 [O] Optimize isAssignmentsReady() 2019-10-13 19:11:38 -04:00
Hykilpikonna 797d962e40 [O] Optimize isGradingReady() with Array.every() 2019-10-13 19:09:55 -04:00
Hykilpikonna 992be72d8d [+] Create course page 2019-10-13 18:56:06 -04:00
Hykilpikonna 375712c0a8 [F] Fix logic error 2019-10-13 18:32:24 -04:00
Hykilpikonna 0b7ab5923c [+] Add version control to cookies 2019-10-13 18:23:36 -04:00
Hykilpikonna d077818508 [F] Make corrections after reverts 2019-10-13 18:23:25 -04:00
Hykilpikonna c991b75384 [-] Revert "[+] Save username to cookies on login"
This reverts commit 5441592d68.
2019-10-13 18:03:18 -04:00
Hykilpikonna 9f59f517b5 [-] Revert "[+] Return username with token"
This reverts commit e28f68aae3.
2019-10-13 18:03:05 -04:00
Hykilpikonna 6654f78486 [-] Revert "[+] Create token interface"
This reverts commit 353a623e5f.
2019-10-13 18:02:45 -04:00
Hykilpikonna 206fef682f [-] Revert "[U] Update http-utils to use token.token"
This reverts commit 543b0ddefe.
2019-10-13 18:02:12 -04:00
Hykilpikonna 3e454423a1 [-] Revert "[U] Also include username when posting request"
This reverts commit 6607afc898.
2019-10-13 18:02:00 -04:00
Hykilpikonna 8326a9e923 [-] Revert "[U] Update variable types"
This reverts commit 6d191f04fb.
2019-10-13 18:01:46 -04:00
Hykilpikonna 5013830815 [-] Revert "[F] Fix http-utils constructor type error"
This reverts commit 0f93a3581f.
2019-10-13 18:00:56 -04:00
Hykilpikonna e4ba53f460 [-] Revert "[O] Optimize token storage"
This reverts commit 846f26db75.
2019-10-13 17:59:07 -04:00
Hykilpikonna fabdc10467 [-] Revert "[U] Use response.data.courses for courses"
This reverts commit 8caab70b25.
2019-10-13 17:57:39 -04:00
Hykilpikonna 91488c6a9f [-] Revert "[+] Save csrf token"
This reverts commit d4df0c3562.
2019-10-13 17:39:25 -04:00
Hykilpikonna f346364633 [O] Make value.min exactly the min value 2019-10-13 17:35:32 -04:00
Hykilpikonna 34e482e8a9 [+] Define value.max for line chart 2019-10-13 17:35:16 -04:00
Hykilpikonna 9aaf662a60 [F] Use scoreId instead of assignmentId when requesting 2019-10-13 15:27:02 -04:00
Hykilpikonna 0b65ebbb00 [+] Parse score_id when parsing 2019-10-13 15:26:32 -04:00
Hykilpikonna 74d1c88f82 [+] Add scoreId to Assignment model 2019-10-13 15:26:22 -04:00
Hykilpikonna 70c8375810 [+] Listen mark as read event 2019-10-13 15:21:00 -04:00
Hykilpikonna b554128337 [+] Implement mark assignment as read 2019-10-13 15:20:49 -04:00
Hykilpikonna 4a8ce2ca18 [O] Use App.http instead of this.http 2019-10-13 15:12:00 -04:00
Hykilpikonna a2df22c1da [O] Make http client static 2019-10-13 15:07:07 -04:00
Hykilpikonna 047ceda252 [-] Remove unnecessary unreadTable() function 2019-10-13 14:53:00 -04:00
Hykilpikonna c94435360f [O] Adjust expansion background clor 2019-10-13 14:51:47 -04:00
Hykilpikonna ed0b7a9740 [O] Use @click instead of onclick 2019-10-13 14:49:44 -04:00
Hykilpikonna b168c4bb82 [+] Call mark as read on click 2019-10-13 14:48:28 -04:00
Hykilpikonna 5078011f40 [+] Add markAsRead() function 2019-10-13 14:47:56 -04:00
Hykilpikonna 1428953cb7 [O] Remove button borders 2019-10-13 14:43:54 -04:00
Hykilpikonna 7f962704d2 [O] Make button paddings smaller 2019-10-13 14:43:44 -04:00
Hykilpikonna 2970263db5 [O] Add left margin 2019-10-13 14:43:31 -04:00
Hykilpikonna 91d9fb90ac [O] Remove button background 2019-10-13 14:43:22 -04:00
Hykilpikonna adb28089b9 [O] Make the button's size mini 2019-10-13 14:42:49 -04:00
Hykilpikonna 6f4a012ac4 [O] Use button instead 2019-10-13 14:42:29 -04:00
Hykilpikonna b807a41496 [O] Make background lighter 2019-10-13 14:08:29 -04:00
Hykilpikonna 02f1fb797f [O] Set color for mark as read 2019-10-13 14:07:54 -04:00
Hykilpikonna caee9117b3 [O] Adjust padding 2019-10-13 14:07:45 -04:00
Hykilpikonna 48cec4c0f0 [+] Add mark-as-read span 2019-10-13 14:07:33 -04:00
Hykilpikonna d4df0c3562 [+] Save csrf token 2019-10-12 20:36:55 -04:00
Hykilpikonna 8caab70b25 [U] Use response.data.courses for courses 2019-10-12 20:36:22 -04:00
Hykilpikonna 846f26db75 [O] Optimize token storage 2019-10-12 20:35:59 -04:00
Hykilpikonna ea2bdb226d [U] Update key names 2019-10-12 20:19:09 -04:00
Hykilpikonna 0f93a3581f [F] Fix http-utils constructor type error 2019-10-12 20:17:00 -04:00
Hykilpikonna 6607afc898 [U] Also include username when posting request 2019-10-12 20:14:57 -04:00
Hykilpikonna 543b0ddefe [U] Update http-utils to use token.token 2019-10-12 20:14:33 -04:00
Hykilpikonna 6d191f04fb [U] Update variable types 2019-10-12 20:14:08 -04:00
Hykilpikonna 353a623e5f [+] Create token interface 2019-10-12 20:11:56 -04:00
Hykilpikonna e28f68aae3 [+] Return username with token 2019-10-12 20:09:22 -04:00
Hykilpikonna 5441592d68 [+] Save username to cookies on login 2019-10-12 20:09:04 -04:00
Hykilpikonna a8e96b142d [+] Add google analytics tag 2019-10-07 21:31:46 -04:00
Hykilpikonna 4527b58084 [O] Optimize alignment for small screens 2019-10-05 20:36:00 -04:00
Hykilpikonna 80d14c1400 [O] Limit max width 2019-10-05 20:30:00 -04:00
Hykilpikonna 86ed26afeb [U] Pre-release v0.3.2.452 2019-10-05 20:15:30 -04:00
Hykilpikonna 35c4147cc1 [O] Lighten drop shadow 2019-10-05 20:13:06 -04:00
Hykilpikonna 711e69ddfb [O] Make font color darker 2019-10-05 20:07:26 -04:00
Hykilpikonna 90d9cac19a [+] Add dynamic assignment type coloring 2019-10-05 20:07:10 -04:00
Hykilpikonna 61593d7d23 [O] Adjust el-col width 2019-10-05 20:02:11 -04:00
Hykilpikonna 7c41e7adcc [O] Add right margins 2019-10-05 20:02:00 -04:00
Hykilpikonna 0215be8681 [O] Make days from now font smaller 2019-10-05 20:01:46 -04:00
Hykilpikonna 1139384ab3 [O] Make assignment type looks like Veracross 2019-10-05 19:57:40 -04:00
Hykilpikonna 45174422e9 [+] Add days from now 2019-10-05 19:57:18 -04:00
Hykilpikonna 2077d784fd [O] Make formatDate return moment instead 2019-10-05 19:55:41 -04:00
Hykilpikonna fa341101a8 [+] Add assignment color variables 2019-10-05 19:47:15 -04:00
Hykilpikonna 1959ac589d [O] show entry box for assignment type 2019-10-05 19:43:46 -04:00
Hykilpikonna 5744493ac3 [+] Add padding to first child 2019-10-05 19:43:06 -04:00
Hykilpikonna 64eb4530af [O] Only show top shadow for first child 2019-10-05 19:22:44 -04:00
Hykilpikonna d38fdfaced [O] Brighten percent score emphesis 2019-10-05 19:20:53 -04:00
Hykilpikonna f76484f824 [O] Adjust el-col width 2019-10-05 19:20:35 -04:00
Hykilpikonna ef6adb0a27 [O] Add entry-box class to span tags 2019-10-05 19:20:22 -04:00
Hykilpikonna ca80df9541 [O] Separate span box class 2019-10-05 19:19:41 -04:00
Hykilpikonna 828907120b [O] Add top shadow 2019-10-05 19:18:43 -04:00
Hykilpikonna 460074b18a [O] Adjust background color 2019-10-05 19:17:00 -04:00
Hykilpikonna 104bd4498e [+] Emphasize percentage score 2019-10-05 17:19:30 -04:00
Hykilpikonna 97082b1e55 [O] Separate percent symbol into new span 2019-10-05 17:18:58 -04:00
Hykilpikonna c9561fba71 [F] Fix "v-for should have explicit keys" 2019-10-05 17:12:31 -04:00
Hykilpikonna 8aa913fe09 [+] Display score in percentage 2019-10-05 17:08:55 -04:00
Hykilpikonna a598521491 [O] Emphasise current score 2019-10-05 17:06:24 -04:00
Hykilpikonna 6461456c16 [O] Style span box to look the same as Veracross 2019-10-05 17:04:35 -04:00
Hykilpikonna b4f697bdea [O] Add css selector classes to spans 2019-10-05 17:01:50 -04:00
Hykilpikonna 721295b9d1 [+] Separate <span>s 2019-10-05 17:00:47 -04:00
Hykilpikonna 70c6e74623 [O] Align grade to the right 2019-10-05 16:59:30 -04:00
Hykilpikonna 1348ca27ab [O] Align text left 2019-10-05 16:58:55 -04:00
Hykilpikonna 116592e436 [+] Add css selector classes to cols 2019-10-05 16:56:31 -04:00
Hykilpikonna 19284a4ddf [O] Combine type and description cols 2019-10-05 16:55:05 -04:00
Hykilpikonna 35e603b850 Revert "[-] Remove el-row and el-col"
This reverts commit 882c7bb35e.
2019-10-05 16:54:19 -04:00
Hykilpikonna 882c7bb35e [-] Remove el-row and el-col 2019-10-05 16:53:51 -04:00
Hykilpikonna 9c8ce3a7f2 [O] Display formatted date 2019-10-05 16:50:49 -04:00
Hykilpikonna 8a6af65786 [+] Create method to format date with moment 2019-10-05 16:50:32 -04:00
Hykilpikonna 4633bd902c [+] Install moment dependency 2019-10-05 16:35:47 -04:00
Hykilpikonna 10fcb8a2f6 [O] Add padding 2019-10-05 16:29:10 -04:00
Hykilpikonna 3ad47eddfd [O] Adjust col width 2019-10-05 16:28:58 -04:00
Hykilpikonna 636f1474bd [O] Vertically center row content 2019-10-05 16:28:48 -04:00
Hykilpikonna ebc9fac4dc [+] Make unread row higher 2019-10-05 16:28:32 -04:00
Hykilpikonna a0e5db6ed9 [+] Parse typeId in jsonUtils 2019-10-05 16:26:19 -04:00
Hykilpikonna f8acd8e222 [+] Add typeId to Assignment model 2019-10-05 16:26:02 -04:00
Hykilpikonna 7b21f82024 [+] Create a table 2019-10-05 16:19:17 -04:00
Hykilpikonna 240cd7ce69 [+] Use unreadEntry components 2019-10-05 16:14:55 -04:00
Hykilpikonna 8a4f9c6f79 [+] Import UnreadEntry component 2019-10-05 16:14:42 -04:00
Hykilpikonna 16f5865c4c [O] Also cache unread assignments 2019-10-05 16:14:31 -04:00
Hykilpikonna 25c3d8e6c1 [+] Create unread-entry component 2019-10-05 16:10:18 -04:00
Hykilpikonna e1584c80a5 [+] Add demo unread entry 2019-10-05 16:05:39 -04:00
Hykilpikonna 89dc493edf [+] Use el-table but comment out 2019-10-05 16:05:14 -04:00
Hykilpikonna aa59075939 [+] Only display expantion when unread messages != 0 2019-10-05 15:50:44 -04:00
Hykilpikonna c55beaf30d [-] Remove debug height declaration 2019-10-05 15:49:27 -04:00
Hykilpikonna 67f1343744 [-] Remove duplicate vertical center code 2019-10-05 15:43:17 -04:00
Hykilpikonna 6b0e0fc8e8 [U] Vertical center main cards 2019-10-05 15:42:38 -04:00
Hykilpikonna af41ad5f53 [O] Separate vertical centering css selector 2019-10-05 15:42:06 -04:00
Hykilpikonna 622cd524e5 [O] Make size larger for debug 2019-10-05 15:41:52 -04:00
Hykilpikonna 7266cf0d80 [+] Make content background darker 2019-10-05 15:39:44 -04:00
Hykilpikonna 47fc11ed96 [+] Add custom padding to content 2019-10-05 15:39:32 -04:00
Hykilpikonna fd927991c7 [F] Remove card padding for styling issues 2019-10-05 15:37:44 -04:00
Hykilpikonna 4b5e6d9856 [+] Add 'main' css class to avoid conflict 2019-10-05 15:32:07 -04:00
Hykilpikonna 3aa5ef9c95 [+] Add notfication div 2019-10-05 15:31:27 -04:00
Hykilpikonna ded76b774c [O] Vertically center content 2019-10-05 15:30:04 -04:00
Hykilpikonna 1bcf2f7410 [O] Make the content hight constant 2019-10-05 15:29:50 -04:00
Hykilpikonna d8cce9ae11 [O] Expand the max hight 2019-10-05 15:29:22 -04:00
Hykilpikonna b4f29559cf [F] Remove global declaration of height 2019-10-05 15:28:08 -04:00
Hykilpikonna a7ea833860 [O] Wrap content in div instead 2019-10-05 15:20:50 -04:00
Hykilpikonna 9c54033343 [+] Wrap course-card around a parent card 2019-10-05 15:11:18 -04:00
Hykilpikonna 7b9dab9d78 [F] Fix rounding issue when detecting necessity of grading api 2019-10-05 14:34:47 -04:00
Hykilpikonna 7f094cbafb [F] Fix typo: should be 'TOTAL_MEAN' instead of 'TOTAL_AVERAGE' 2019-10-05 14:31:08 -04:00
Hykilpikonna c4b8542b5b [F] Skip courses without graded assignments instead of without any assignments 2019-10-05 14:27:18 -04:00
Hykilpikonna 0345d37b2c [U] Pre-release v0.3.1.382 2019-10-05 14:11:32 -04:00
Hykilpikonna 97818c45f5 [-] Remove drop shadow 2019-10-05 14:10:26 -04:00
Hykilpikonna 1a09c7311e [O] Make font size smaller 2019-10-05 14:10:17 -04:00
Hykilpikonna b658f6f707 [O] Make text italic 2019-10-05 14:10:08 -04:00
Hykilpikonna 1d63e4865b [M] Move main color to theme 2019-10-05 14:05:26 -04:00
Hykilpikonna 78fbd342d9 [O] Apply nowrap to all cards 2019-10-05 14:04:06 -04:00
Hykilpikonna 141c192017 [+] Add github link to splash 2019-10-05 14:00:07 -04:00
Hykilpikonna 87e44b83f7 [+] Add github link constant 2019-10-05 13:59:50 -04:00
Hykilpikonna 1064831379 [O] Separate version as a constant 2019-10-05 13:59:37 -04:00
Hykilpikonna 1e42ef7873 [O] Add text shadow 2019-10-05 13:54:56 -04:00
Hykilpikonna c8327f3774 [O] Replace hex with var 2019-10-05 13:51:26 -04:00
Hykilpikonna 69cd4ff050 [+] Use default theme in base div 2019-10-05 13:51:05 -04:00
Hykilpikonna 027f8bfd34 [+] Create theme css class 2019-10-05 13:50:50 -04:00
Hykilpikonna 70ede22c18 [O] Remove space between number and '%' 2019-10-05 13:45:04 -04:00
Hykilpikonna fd74298b0b [+] Add some right margin 2019-10-05 13:44:35 -04:00
Hykilpikonna d47bae00ff [+] Change color of unread text as well 2019-10-05 13:43:32 -04:00
Hykilpikonna 0c91b1b598 [U] Update css implementation 2019-10-05 13:42:13 -04:00
Hykilpikonna 6ba789c7cf [M] Move unread class up a level 2019-10-05 13:41:56 -04:00
Hykilpikonna 54b70f6a1c [F] Fix inversed condition 2019-10-05 13:39:08 -04:00
Hykilpikonna ac32a02ff4 [O] Make 'update' plural only when count >= 2 2019-10-05 13:38:22 -04:00
Hykilpikonna 993578277e [+] Add 'new update' text 2019-10-05 13:38:00 -04:00
Hykilpikonna 48fb8c7d4c [O] Optimize unread count with caching 2019-10-05 13:36:56 -04:00
Hykilpikonna 0f6f27aa15 [O] Add some paddings to make it larger 2019-10-05 13:34:56 -04:00
Hykilpikonna b2947e9ef6 [+] Add shadow 2019-10-05 13:18:17 -04:00
Hykilpikonna c5ef8a297b [F] Fix css syntax: Stacked class vs stacked element 2019-10-05 13:16:31 -04:00
Hykilpikonna 02ca6b8d47 [+] Add dynamic coloring 2019-10-05 13:15:20 -04:00
Hykilpikonna 98b92b4dfb [+] Add dynamic class based on unread count 2019-10-05 13:14:03 -04:00
Hykilpikonna 1c2afdfa52 [O] Add block around the update number 2019-10-05 13:13:45 -04:00
Hykilpikonna 55b4bd9173 [+] Encapsulate method to count unread assignments 2019-10-05 13:10:34 -04:00
Hykilpikonna e422c03ca5 [-] Remove centering 2019-10-05 13:00:02 -04:00
Hykilpikonna 831ef16a2f [+] Display unread 2019-10-05 12:59:45 -04:00
Hykilpikonna 0deb0ed719 [F] Fix key typo: is_unread instead of unread 2019-10-05 12:56:10 -04:00
Hykilpikonna f3d44ceda2 [+] Parse id and unread 2019-10-05 10:24:42 -04:00
Hykilpikonna cb11db6201 [+] Add field id and unread to Assignment type 2019-10-05 10:23:46 -04:00
Hykilpikonna b52f3d2617 [M] Rename type Grade to type Assignment 2019-10-05 10:22:40 -04:00
Hykilpikonna 39cd5bf1a2 [F] Fix grid misplacement problem 2019-10-02 08:13:17 -04:00
Hykilpikonna 19f03717bc [O] Go back to using grid 2019-10-02 08:12:53 -04:00
Hykilpikonna 6d728c25ae [O] Center grade display 2019-10-01 21:45:36 -04:00
Hykilpikonna c8daa2a06e [+] Fix the numeric grade to 2 digits 2019-10-01 21:41:57 -04:00
Hykilpikonna 4d41e679dd [O] Make grade font size match Veracross 2019-10-01 21:29:56 -04:00
Hykilpikonna acfebf9ff3 [+] Show course card for every course 2019-10-01 21:29:35 -04:00
Hykilpikonna 5af1b1e5a1 [O] Add auto margin top and bottoms 2019-10-01 21:19:22 -04:00
Hykilpikonna bc9a4f00c1 [+] Align left and right 2019-10-01 21:19:08 -04:00
Hykilpikonna f13f3a155c [-] Remove the grid 2019-10-01 21:09:50 -04:00
Hykilpikonna a19edb8aa7 [+] Display numeric grade and letter grade 2019-10-01 21:01:15 -04:00
Hykilpikonna 6a0500acb2 [-] Remove unnecessary span 2019-10-01 20:54:29 -04:00
Hykilpikonna 96af69508f [O] Limit course name length 2019-10-01 20:50:25 -04:00
Hykilpikonna 3fbc9764e9 [O] Make teacher name italicised 2019-10-01 20:50:12 -04:00
Hykilpikonna a79dcb76af [O] Make teacher name font size match Veracross 2019-10-01 20:49:54 -04:00
Hykilpikonna 86c2fd5df7 [+] Add underline effect on hover 2019-10-01 20:49:18 -04:00
Hykilpikonna 07eaeef133 [+] Add margin to the beginning of course name 2019-10-01 20:49:03 -04:00
Hykilpikonna d1bd0a5667 [O] Change font size to match Veracross 2019-10-01 20:48:44 -04:00
Hykilpikonna 49dad14c84 [O] Adjust spacing 2019-10-01 20:47:57 -04:00
Hykilpikonna 50e3c345d5 [O] Clarify css selector 2019-10-01 20:40:18 -04:00
Hykilpikonna a444dbb32a [+] Add cols to display course name and teacher name 2019-10-01 20:39:53 -04:00
Hykilpikonna 03e212bd37 [+] Add margins to the navigation bar 2019-10-01 20:24:54 -04:00
Hykilpikonna 0325eb13d5 [F] Fix css priority issue 2019-10-01 20:17:37 -04:00
Hykilpikonna bfcb80aa64 [+] Add a title but comment it out 2019-10-01 20:10:48 -04:00
Hykilpikonna 91a6c0c6e4 [+] Optimize card formatting 2019-10-01 20:01:56 -04:00
Hykilpikonna 845cc94513 [+] Add a card in course element 2019-10-01 20:01:47 -04:00
Hykilpikonna 21ccaceb53 [+] Add a testing course element 2019-10-01 20:01:37 -04:00
Hykilpikonna b9f63171f2 [+] Create overall-course component 2019-10-01 19:44:51 -04:00
Hykilpikonna 636d92d1a0 [M] Rename and correct some misc css class names 2019-10-01 19:44:24 -04:00
Hykilpikonna 199ab9e00c [M] Rename graph-average to overall-bar 2019-10-01 19:37:20 -04:00
Hykilpikonna 77541764d1 [M] Rename graph-overall to overall-line 2019-10-01 19:36:09 -04:00
Hykilpikonna 53ae74f9c4 [O] Use absolute positioning to center what's in the center 2019-10-01 19:33:11 -04:00
Hykilpikonna 5bc1d6ba48 [O] Put sign-out button on the right 2019-10-01 19:30:41 -04:00
Hykilpikonna 831ddcf84e [-] Remove debug output 2019-10-01 19:27:00 -04:00
Hykilpikonna 35a85d8e83 [U] Release v0.2.3.315 2019-10-01 19:19:27 -04:00
Hykilpikonna 71b0a6e4dd [-] Remove debug output 2019-10-01 19:18:51 -04:00
Hykilpikonna d4fbd04466 [F] Fix grading history lower than actual 2019-10-01 19:18:33 -04:00
Hykilpikonna 31b3814b1e [O] Ignore 0 when calculating the score 2019-10-01 19:02:41 -04:00
Hykilpikonna 1aff2b0a68 [-] Remove debug output 2019-10-01 19:02:10 -04:00
Hykilpikonna 2567fcadbd [+] Encapsulate method to calculate total-mean average 2019-10-01 18:41:42 -04:00
Hykilpikonna d9e0e9f84e [F] Fix another null case 2019-10-01 18:41:14 -04:00
Hykilpikonna 59b31ea43f [F] Fix key word typo 2019-10-01 18:41:04 -04:00
Hykilpikonna 2c8b3e0f84 [F] Fix null case 2019-10-01 18:40:54 -04:00
Hykilpikonna 6910a7b5ea [F] Fix null pointer caused by grading not existing 2019-10-01 18:30:20 -04:00
Hykilpikonna cbdcfc4ca1 [+] Check gradings after checking assignments 2019-10-01 18:30:02 -04:00
Hykilpikonna feabc336c1 [+] Create method to check course gradings 2019-10-01 18:29:50 -04:00
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
51 changed files with 2617 additions and 381 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.
+5
View File
@@ -6935,6 +6935,11 @@
}
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
+1
View File
@@ -11,6 +11,7 @@
"core-js": "^2.6.5",
"echarts": "^4.2.1",
"element-ui": "^2.11.1",
"moment": "^2.24.0",
"p-wait-for": "^3.1.0",
"v-charts": "^1.19.0",
"vue": "^2.6.10",
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404: Redirecting...</title>
<meta http-equiv = "refresh" content = "0; url = https://vera.hydev.org/" />
</head>
<body>
404 Not Found! Redirecting to (<a href="https://vera.hydev.org">https://vera.hydev.org</a>)...
<script>
window.location.href = 'https://vera.hydev.org';
</script>
</body>
</html>
+10
View File
@@ -25,4 +25,14 @@
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Q615K1KFLC"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-Q615K1KFLC');
</script>
</html>
+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 -
+82 -5
View File
@@ -1,8 +1,85 @@
#app
{
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
font-family: var(--font);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-bottom: 100px;
}
#app-content
{
// Limit max width
max-width: 1300px;
text-align: center;
margin: auto;
}
.theme-default
{
--unread: #ff6c00;
--main: #0c6dad;
--assignment-type-2: #3f991e;
--assignment-type-3: #ff9900;
--assignment-type-4: #b02b02;
--font: 'Avenir', Helvetica, Arial, sans-serif;
}
// ##############
// # Global CSS #
// ##############
.el-card
{
margin: 10px;
padding: 0;
}
.el-card.large
{
height: 494px;
}
// Fix padding
.el-card__body
{
padding-top: 0 !important;
padding-bottom: 0 !important;
}
// Vertical centering
.vertical-center
{
// Vertical center
display: flex;
justify-content: center;
flex-direction: column;
}
// Remove card padding for styling issues
div.el-card.course-card > div.el-card__body
{
padding-right: 0 !important;
padding-left: 0 !important;
}
// Clickable text
.clickable:hover
{
text-decoration: underline;
cursor: pointer;
}
// Non-selectable text
.unselectable
{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
+139 -70
View File
@@ -1,50 +1,41 @@
import {Component, Vue} from 'vue-property-decorator';
import Login from '@/components/login/login';
import Navigation from '@/components/navigation/navigation';
import Overall from '@/pages/overall/overall';
import Overall from '@/pages/overall/overall.vue';
import Constants from '@/constants';
import JsonUtils from '@/utils/json-utils';
import pWaitFor from 'p-wait-for';
import {HttpUtils} from '@/utils/http-utils';
import {GPAUtils} from '@/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 Grade
export interface Assignment
{
type: string,
description: string,
date: string,
complete: string,
include: boolean,
display: boolean,
id: number
scoreId: number
type: string
typeId: number
description: string
date: Date
complete: string
include: boolean
display: boolean
scoreMax: number,
unread: boolean
scoreMax: number
score: number
}
/**
* A course
*/
export interface Course
{
assignmentsId: number,
id: number,
name: string,
teacherName: string,
status: string,
letterGrade?: string,
numericGrade?: number,
level: string,
scaleUp: number,
assignments: Grade[]
gradingPeriod: number
}
@Component({
components: {Login, Navigation, Overall},
components: {Login, Navigation, Overall, Loading, CoursePage},
})
export default class App extends Vue
{
@@ -54,8 +45,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;
@@ -63,8 +57,17 @@ export default class App extends Vue
// Token
public token: string = '';
// Loading text
public loading: string = '';
// Loading error
public loadingError: boolean = false;
// Http Client
public http: HttpUtils = new HttpUtils('');
public static http: HttpUtils = new HttpUtils();
// Instance
public static instance: App;
/**
* This is called when the instance is created.
@@ -73,6 +76,9 @@ export default class App extends Vue
{
// Show splash
console.log(Constants.SPLASH);
// Update instance
App.instance = this;
}
/**
@@ -85,11 +91,14 @@ export default class App extends Vue
// Hide login bar
this.showLogin = false;
// Show loading message
this.logLoading('1. Logging in...');
// Store token
this.token = token;
// Assign token to http client
this.http.token = token;
App.http.token = token;
// Load data
this.loadCoursesAfterLogin();
@@ -100,89 +109,149 @@ export default class App extends Vue
*/
public loadCoursesAfterLogin()
{
this.http.post('/courses', {}).then(response =>
// Show loading message
this.logLoading('2. Loading courses...');
// Post request
App.http.post('/courses', {}).then(response =>
{
// Check success
if (response.success)
{
// Save courses
this.courses = response.data;
this.courses = response.data.map((courseJson: any) => new Course(courseJson));
// Load assignments
this.loadAssignments();
}
else
{
// Show error message TODO: Show it properly
alert(response.data);
}
else throw new Error(response.data);
})
.catch(alert);
.catch(e => this.showError(`Error: Course data failed to load.\n(${e})`));
}
/**
* Load the assignments of the courses
*
* @param courses Courses Json
*/
public loadAssignments()
{
// Show loading message
this.logLoading('3. Loading assignments...');
// Get assignments for all the courses
this.courses.forEach(course =>
{
// Send request to get assignments
this.http.post('/assignments', {id: course.assignmentsId}).then(response =>
App.http.post('/assignments', {'assignmentsId': course.assignmentsId}).then(response =>
{
// Check success
if (response.success)
{
// Load assignments
// Parse json and filter it
course.assignments = JsonUtils.filterAssignments(response.data);
}
else
{
// Show error message TODO: Show it properly
alert(response.data);
course.loadAssignments(response.data);
}
else throw new Error(response.data);
})
.catch(alert);
.catch(e => this.showError(`Error: Assignments data failed to load.\n(${e})`));
});
// Wait for assignments to be ready.
pWaitFor(() => this.isAssignmentsReady()).then(() =>
pWaitFor(() => this.courses.every(c => c.rawAssignments != null)).then(() =>
{
// When the assignments are ready
this.assignmentsReady = true;
// Filter courses
this.filteredCourses = this.courses.filter(c => c.isGraded);
// Check grading algorithms
this.checkGradingAlgorithms();
});
}
/**
* Are assignments ready or not
*
* @returns boolean Ready or not
* Check the courses' grading algorithms. (Total-mean or percent-type)
*/
private isAssignmentsReady(): boolean
private checkGradingAlgorithms()
{
for (const course of this.courses)
// Show loading message
this.logLoading('4. Checking grading algorithms...');
// Loop through all the courses
for (const course of this.filteredCourses)
{
if (course.assignments == null) return false;
let termGrade = +GPAUtils.getTotalMeanAverage(course.computed.termAssignments[Constants.CURRENT_TERM]).toFixed(2);
// Check if total-average grade is the same with percent-type grade
if (course.rawNumericGrade == termGrade)
{
course.grading = {method: 'TOTAL_MEAN', weightingMap: {}};
}
else
{
// Request grading scheme for this course
App.http.post('/grading', {'assignmentsId': course.assignmentsId}).then(response =>
{
// Check success
if (response.success)
{
// Add it to course
course.grading = response.data;
}
else throw new Error(response.data);
})
.catch(e => this.showError(`Error: Grading data failed to load.\n(${e})`))
}
}
return true;
// Wait for done
pWaitFor(() => this.filteredCourses.every(c => c.grading != undefined)).then(() =>
{
// When the assignments are ready
this.assignmentsReady = true;
// Remove loading
this.logLoading('');
})
}
/**
* This is called when a navigation tab is clicked
* Log a message to loading screen
*
* @param tab Tab name
* @param message Message
*/
public onNavigate(tab: string)
private logLoading(message: string)
{
// Debug output TODO: Remove this
console.log(tab);
if (message == '') this.loading = '';
else this.loading += '\n' + message;
}
// Update selected tab
this.selectedTab = tab;
/**
* Show error message on loading screen
*
* @param message Error message
*/
private showError(message: string)
{
this.loadingError = true;
this.loading = message;
}
/**
* Sign out
*/
public signOut()
{
// Clear all cookies
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
// Refresh
window.location.reload();
}
/**
* Select time (Eg. Term 1, Term 2, All Year, etc.)
*
* @param code
*/
public selectTime(code: number)
{
// TODO: Optimize
window.location.reload();
}
}
+15 -5
View File
@@ -1,11 +1,21 @@
<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>
<div id="app" class="theme-default">
<login v-if="showLogin" v-on:login:token="onLogin"></login>
<navigation :courses="filteredCourses"
:activeIndex.sync="selectedTab"
@sign-out="signOut" @select-time="selectTime">
</navigation>
<div id="app-content">
<overall :courses="courses" v-if="selectedTab === 'overall' && assignmentsReady"></overall>
<div id="app-content" v-if="assignmentsReady && loading === ''">
<overall v-if="selectedTab === 'overall'"
:courses="filteredCourses">
</overall>
<course-page v-if="selectedTab.split('/')[0] === 'course'"
:course="filteredCourses.find(c => +c.id === +selectedTab.split('/')[1])">
</course-page>
</div>
<loading v-if="loading !== ''" :text="loading" :error="loadingError"></loading>
</div>
</template>
+114
View File
@@ -0,0 +1,114 @@
<template>
<div id="loading">
<div id="text" :class="message()">
{{message()}}
<div v-if="!error" class="el-loading-spinner">
<svg viewBox="25 25 50 50" class="circular">
<circle cx="50" cy="50" r="20" fill="none" class="path" />
</svg>
</div>
<div v-if="error" id="error-details">
<span v-for="(line, index) in getText()" :style="`font-size: ${-index === 0 ? 16 : 12}px;`">
{{line}}
<br>
</span>
</div>
</div>
<div v-if="!error" id="details">
<span v-for="(line, index) in getText()" :style="`font-size: ${16 - getText().length + index}px;`">
{{line}}
<br>
</span>
</div>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
@Component({
components: {}
})
export default class Loading extends Vue
{
@Prop({required: true}) text: string;
@Prop({required: true}) error: boolean;
getText()
{
return this.text.split('\n');
}
message()
{
return this.error ? 'Error' : 'Loading';
}
}
</script>
<style scoped>
#loading
{
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
box-shadow: inset 0 0 1px 1px rgba(0,0,0,.1);
background: -webkit-linear-gradient(left, rgba(95, 18, 72, 0.4), rgba(42, 81, 117, 0.4) 100%);
text-align: center;
}
.Error
{
color: #ffdddd !important;
}
#text
{
color: white;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 46px;
}
#details
{
width: 100%;
position: absolute;
bottom: 0;
left: 0;
margin-top: -5px;
font-size: 16px;
color: #f9f9f9;
}
#error-details
{
font-size: 16px;
}
.el-loading-spinner
{
top: unset !important;
margin-top: 0 !important;
width: unset !important;
position: unset !important;
}
.el-loading-spinner .path
{
stroke: white;
}
</style>
+46 -9
View File
@@ -1,6 +1,7 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import {Component, Vue} from 'vue-property-decorator';
import Constants from '@/constants';
import {HttpUtils} from '@/utils/http-utils';
import App from '@/components/app/app';
import VersionUtils from '@/utils/version-utils';
/**
* This component handles user login, and obtains data from the server.
@@ -16,9 +17,6 @@ export default class Login extends Vue
public loading: boolean = false;
public error: String = '';
@Prop()
public http?: HttpUtils;
/**
* This is called when the instance is created.
*/
@@ -27,9 +25,39 @@ export default class Login extends Vue
// Check login cookies
if (this.$cookies.isKey('va.token'))
{
// Already contains valid token / TODO: Validate
this.$emit('login:token', this.$cookies.get('va.token'));
// Check cookies version
if (this.needToUpdateCookies())
{
console.log('Version Updated! Clearing cookies...');
// Clear all cookies
this.$cookies.keys().forEach(key => this.$cookies.remove(key));
}
else
{
// Already contains valid token / TODO: Validate
// TODO: Update token each access
this.$emit('login:token', this.$cookies.get('va.token'));
}
}
else
{
console.log('Cookies doesn\'t exist');
}
}
/**
* Check version number
*
* @returns boolean Need to clear cookies or not
*/
public needToUpdateCookies(): boolean
{
// Version doesn't exist
if (!this.$cookies.isKey('va.version')) return true;
// If the current version is less than the min supported version
return VersionUtils.compare(this.$cookies.get('va.version'), Constants.MIN_SUPPORTED_VERSION) == -1;
}
/**
@@ -41,14 +69,15 @@ export default class Login extends Vue
this.loading = true;
// Fetch request
(<HttpUtils> this.http).post('/login', {username: this.username, password: this.password})
App.http.post('/login', {username: this.username, password: this.password})
.then(response =>
{
// Check success
if (response.success)
{
// Save token to cookies
this.$cookies.set('va.token', response.data, '7d');
this.$cookies.set('va.token', response.data, '27d');
this.$cookies.set('va.version', Constants.VERSION, '27d');
// Call custom event with token
this.$emit('login:token', response.data);
@@ -70,4 +99,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();
}
}
+15 -12
View File
@@ -5,21 +5,24 @@
<img alt="Vue logo" src="../../assets/logo.png">
<h1>Veracross Analyzer</h1>
<form id="login-form">
<el-input v-model="username"
placeholder="School Username"
:class="{'input-error': error !== ''}"
@keyup.enter.native="onEnter">
</el-input>
<el-input v-model="username"
placeholder="School Username"
:class="{'input-error': error !== ''}">
</el-input>
<el-input v-model="password"
placeholder="Veracross Password"
show-password=""
:class="{'input-error': error !== ''}"
@keyup.enter.native="onEnter">
</el-input>
<el-input v-model="password"
placeholder="Veracross Password"
show-password=""
:class="{'input-error': error !== ''}">
</el-input>
<div class="el-form-item__error custom">{{error}}</div>
<div class="el-form-item__error custom">{{error}}</div>
<el-button plain type="primary" @click="onLoginClick" :loading="loading">Login</el-button>
<el-button plain type="primary" @click="onLoginClick" :loading="loading">Login</el-button>
</form>
</div>
</div>
</div>
+96
View File
@@ -15,3 +15,99 @@
border-bottom-width: 0;
}
}
#sign-out-button
{
// Float right
position: absolute;
right: 0;
// Set width and height
height: 60px;
width: 110px;
}
#nav-grading-period
{
// Float right
position: absolute;
right: 110px;
// Margins
margin-top: 12px;
margin-bottom: 12px;
}
#nav-title
{
// Float left
position: absolute;
left: 0;
// Set height
height: 60px;
// Center text
align-items: center;
// Margins
margin-left: 20px;
margin-right: 8px;
// Font
font: bold 16px arial;
display: inline-flex;
// Shadow effect
-webkit-background-clip: text;
background-color: #b1b1b1;
color: transparent;
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.85);
// Make it non-clickable
pointer-events: none;
}
#next-course
{
// Down center
width: 50%;
position: absolute;
bottom: 0;
left: 25%;
padding-top: 2px;
box-shadow: 0 -2px 9px 0 #ecebeb;
}
footer
{
position: fixed;
left: 0;
bottom: 0;
width: 100%;
}
#prev-course
{
// Up center
width: 50%;
position: absolute;
top: 61px;
left: 25%;
padding-bottom: 2px;
box-shadow: 0 2px 9px 0 #ecebeb;
}
.nav-course-operations
{
// Background
background-color: rgba(214, 214, 214, 0.67);
opacity: 0.85;
// Font
font-size: 14px;
color: #ab8585;
// Cursor
cursor: pointer;
}
+161 -5
View File
@@ -1,5 +1,9 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import Constants from '@/constants';
import App from '@/components/app/app';
import {CourseUtils} from '@/utils/course-utils';
import {FormatUtils} from '@/utils/format-utils';
import pWaitFor from 'p-wait-for';
import Course from '@/logic/course';
/**
* This component is the top navigation bar
@@ -9,9 +13,63 @@ import Constants from '@/constants';
})
export default class Navigation extends Vue
{
public activeIndex: string = 'overall';
@Prop({required: true}) activeIndex: string;
@Prop() courses: any;
@Prop({required: true}) courses: Course[];
private gradingPeriod: string = 'All Year';
// Instance
public static instance: Navigation;
/**
* This is called when the instance is created.
*/
public created()
{
// Check selected time
if (!this.$cookies.isKey('va.grading-period'))
{
this.$cookies.set('va.grading-period', this.gradingPeriod, '10y');
}
this.gradingPeriod = this.$cookies.get('va.grading-period');
}
/**
* This is called when the instance is loaded.
*/
public mounted()
{
// Set instance
Navigation.instance = this;
// Set history state
let url = '/' + window.location.hash;
if (url == '/' || url == '') url = '/#overall';
window.history.replaceState({lastTab: url.substring(1)}, '', url);
// Update initial index after loading is done
pWaitFor(() => this.courses.length > 1 && App.instance.loading != '').then(() =>
{
this.updateIndex(url.substring(2), false);
});
// Create history state listener
window.onpopstate = e =>
{
if (e.state)
{
// Restore previous tab
console.log(`onPopState: Current: ${this.activeIndex}, Previous: ${e.state.lastTab}`);
this.updateIndex(e.state.lastTab, false);
}
};
}
public formatCourseIndex(course: Course)
{
return CourseUtils.formatTabIndex(course);
}
/**
* This function is called when the selection changes.
@@ -22,9 +80,107 @@ export default class Navigation extends Vue
public onSelect(index: string, indexPath: string)
{
// Update active index
this.activeIndex = index;
this.updateIndex(index);
}
/**
* Update index
*
* @param newIndex New index
* @param history Record in history or not (Default true)
*/
public updateIndex(newIndex: string, history?: boolean)
{
// Call custom event
this.$emit('navigation:select', this.activeIndex);
this.$emit('update:activeIndex', newIndex);
// Record or not
if (history == null || history)
{
// Check url
let url = `/#${newIndex}`;
// Push history state
window.history.pushState({lastTab: newIndex}, '', url);
}
// Update title
document.title = 'Veracross Analyzer - ' + this.getTitle(newIndex);
}
/**
* Get title for index
*
* @param index Index
*/
public getTitle(index: string)
{
// Course
if (index.startsWith('course'))
{
return this.findCourse(index.split('/')[1], 0).name;
}
// Others
return FormatUtils.toTitleCase(index);
}
/**
* Move to the next course
*
* @param indexOffset Index offset (Eg. 1 for next)
*/
public nextCourse(indexOffset: number)
{
// Set tab to the next index
this.updateIndex(CourseUtils.formatTabIndex(this.findNextCourse(indexOffset)))
}
/**
* Find the next course
*
* @param indexOffset Index offset (Eg. 1 for next)
*/
public findNextCourse(indexOffset: number)
{
return this.findCourse(this.activeIndex.split('/')[1], indexOffset);
}
/**
* Find course
*
* @param courseId Course ID
* @param indexOffset Index offset (Eg. 1 for next)
*/
public findCourse(courseId: string, indexOffset: number)
{
// Find current course index
let courseIndex = this.courses.findIndex(c => c.id == +courseId);
// Find next course
return this.courses[courseIndex + indexOffset];
}
/**
* Select grading period
*
* @param command Term 1, Term 2, All Year, etc.
*/
public selectGradingPeriod(command: string)
{
this.gradingPeriod = command;
this.$cookies.set('va.grading-period', command, '10y');
// Call event
this.$emit('select-time', this.getSelectedGradingPeriod());
}
/**
* Get code for selected time
*/
public getSelectedGradingPeriod(): number
{
if (this.gradingPeriod == 'All Year') return -1;
else return +this.gradingPeriod.replace('Term ', '') - 1;
}
}
+34 -3
View File
@@ -1,18 +1,49 @@
<template>
<div id="navigation">
<el-menu class="centered" :default-active="activeIndex" mode="horizontal" @select="onSelect">
<el-menu style="margin-bottom: 10px;" class="centered" mode="horizontal"
:default-active="activeIndex" @select="onSelect">
<div id="nav-title">
Veracross Analyzer
</div>
<el-menu-item index="overall">Overall</el-menu-item>
<el-submenu index="courses">
<template slot="title">Courses</template>
<el-menu-item v-for="course in courses"
:index="`course-${course.name}`"
:index="formatCourseIndex(course)"
:key="course.name">{{course.name}}</el-menu-item>
</el-submenu>
<!-- Grading period selection -->
<el-dropdown id="nav-grading-period" @command="selectGradingPeriod">
<el-button type="primary" size="medium">
{{gradingPeriod}}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="Term 1">Term 1</el-dropdown-item>
<el-dropdown-item command="Term 2">Term 2</el-dropdown-item>
<el-dropdown-item command="Term 3" disabled>Term 3</el-dropdown-item>
<el-dropdown-item command="Term 4" disabled>Term 4</el-dropdown-item>
<el-dropdown-item command="All Year" divided>All Year</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button @click="$emit('sign-out')" id="sign-out-button" type="text">Sign Out</el-button>
</el-menu>
<div class="line"></div>
<!-- Previous course / Next course (Only when the page is courses) -->
<div v-if="activeIndex.includes('course') && findNextCourse(-1) != null"
@click="nextCourse(-1)" id="prev-course" class="nav-course-operations unselectable">
PREVIOUS COURSE
</div>
<footer>
<div v-if="activeIndex.includes('course') && findNextCourse(1) != null"
@click="nextCourse(1)" id="next-course" class="nav-course-operations unselectable">
NEXT COURSE
</div>
</footer>
</div>
</template>
+48 -5
View File
@@ -3,16 +3,59 @@
*/
export default class Constants
{
/**
* Base url for api access
* TODO: Use https for actual usage
*/
/** Base url for api access */
public static API_URL: string = 'https://va.hydev.org/api';
/** Current version */
public static VERSION: string = '0.4.2.912';
/** Minimum version that still supports the same cookies */
public static MIN_SUPPORTED_VERSION: string = '0.3.4.561';
public static GITHUB: string = 'https://github.com/HyDevelop/VeracrossAnalyzer.Client';
public static SPLASH: string =
'. , ,---. | \n' +
'| |. , |---|,---.,---.| , .,---,,---.,---.\n' +
' \\ / >< | || |,---|| | | .-\' |---\'| \n' +
' `\' \' ` ` \'` \'`---^`---\'`---|\'---\'`---\'` \n' +
' v1.1.0 `---\' '
' `---\' \n' +
` Version v${Constants.VERSION} by Hykilpikonna (YGui21)\n` +
` Github: ${Constants.GITHUB}`;
// Graph Theme
public static THEME =
{
// Colors
colors:
[
'#19d4ae',
'#5ab1ef',
'#fa6e86',
'#ffb980',
'#0067a6',
'#c4b4e4',
'#d87a80',
'#9cbbff',
'#d9d0c7',
'#87a997',
'#d49ea2',
'#5b4947',
'#7ba3a8',
'#fc97af',
'#919e8b',
'#d7ab82',
'#6e7074',
'#61a0a8',
'#efa18d',
'#787464',
'#cc7e63',
'#724e58',
'#4b565b'
]
};
// 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 CURRENT_TERM = 1;
}
+181
View File
@@ -0,0 +1,181 @@
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 Navigation from '@/components/navigation/navigation';
import {GPAUtils} from '@/utils/gpa-utils';
export default class Course
{
id: number;
assignmentsId: number;
name: string;
teacherName: string;
status: string;
rawAssignments: Assignment[];
rawLetterGrade?: string;
rawNumericGrade?: number;
level: string;
scaleUp: number;
grading:
{
method: string
weightingMap: {[index: string]: number}
};
computed:
{
termAssignments: Assignment[][]
allYearGrade: number
};
/**
* Construct a course with a course json object
*
* @param courseJson Course json object
*/
constructor(courseJson: any)
{
this.id = courseJson.id;
this.assignmentsId = courseJson.assignmentsId;
this.name = FormatUtils.parseText(courseJson.name).trim();
this.teacherName = courseJson.teacherName;
this.status = courseJson.status;
this.rawLetterGrade = courseJson.letterGrade;
this.rawNumericGrade = courseJson.numericGrade;
// Other api issue
if (this.rawLetterGrade == '')
{
this.rawNumericGrade = undefined;
this.rawLetterGrade = undefined;
}
// Level and scaleUp
let level = CourseUtils.detectLevel(this.name);
if (level != undefined)
{
this.level = level.level;
this.scaleUp = level.scaleUp;
}
else this.level = 'Unknown';
this.grading = courseJson.grading;
}
/**
* Load in assignments data
*
* @param data Assignments data
*/
loadAssignments(data: any)
{
// Load assignments
// Parse json and filter it
this.rawAssignments = JsonUtils.filterAssignments(data);
// Sort by date (Latest is at 0)
this.rawAssignments.sort((a, b) => b.date.getTime() - a.date.getTime());
// Filter assignments into terms
let termAssignments: Assignment[][] = [[], [], [], []];
let currentTerm = 0;
// Loop through it by time order
this.rawAssignments.forEach(a => termAssignments[a.gradingPeriod].push(a));
// Set computed data
this.computed = {termAssignments: termAssignments, allYearGrade: -1};
}
/**
* Is graded or not
*/
get isGraded(): boolean
{
// Skip future or past courses
if (this.status != 'active') return false;
// Skip courses without levels TODO: Ask for user input
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;
// Skip if there are no grading scale
// if (course.grading.method == 'NOT_GRADED') return;
// Is graded
return true;
}
/**
* Get assignments of the selected time
*/
get assignments(): Assignment[]
{
let timeCode = Navigation.instance.getSelectedGradingPeriod();
// All year
if (timeCode == -1)
{
return this.rawAssignments;
}
// Specific time
return this.computed.termAssignments[timeCode];
}
// TODO: Optimize this
private letterGradeComputed = false;
/**
* Get letter grade
*/
get letterGrade(): string
{
// Cached
if (this.rawLetterGrade != undefined && this.letterGradeComputed)
return this.rawLetterGrade;
this.letterGradeComputed = true;
// Get scale
let scale = GPAUtils.findScale(this.numericGrade);
// Scale not found
if (scale == undefined) return this.rawLetterGrade = '--';
// Return
return this.rawLetterGrade = scale.letter;
}
private numericGradeComputed = false;
/**
* Get numeric grade
*/
get numericGrade(): number
{
// Cached
if (this.rawNumericGrade != undefined && this.numericGradeComputed)
return this.rawNumericGrade;
this.numericGradeComputed = true;
// Calculate
if (this.grading.method == 'PERCENT_TYPE')
{
return this.rawNumericGrade = GPAUtils.getPercentTypeAverage(this, this.assignments);
}
if (this.grading.method == 'TOTAL_MEAN')
{
return this.rawNumericGrade = GPAUtils.getTotalMeanAverage(this.assignments);
}
// Error
return -1;
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
const VCharts = require('v-charts');
import App from './components/app/app.vue';
import VueCookies from 'vue-cookies';
const VCharts = require('v-charts');
Vue.config.productionTip = false;
// Use Element UI
@@ -0,0 +1,80 @@
<template>
<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>
</div>
<AssignmentEntry v-for="assignment of filteredAssignments" :key="assignment.id"
:assignment="assignment" :unread="false"
backgroundColor="#ffffff" narrow="true">
</AssignmentEntry>
</el-card>
</div>
</template>
<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';
@Component({
components: {AssignmentEntry}
})
export default class AssignmentTypeHead extends Vue
{
@Prop({required: true}) typeName: string;
@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;
}
}
</script>
<style lang="scss" scoped>
#type-info-card
{
height: 60px;
}
#type-name
{
// Font
font-size: 22px;
color: var(--main);
// Center
height: 60px;
line-height: 60px;
// Alignment
padding-left: 20px;
float: left;
}
#type-average
{
// Font
font-size: 14px;
color: #8db3e4;
// Center
height: 60px;
line-height: 64px;
// Alignment
float: left;
margin-left: 15px;
display: inline-block;
}
</style>
+20
View File
@@ -0,0 +1,20 @@
// Card
.el-card.course-card
{
// Margins
margin-right: 20px;
margin-left: 20px;
// Limit name length
white-space: nowrap;
// Expansion color
background: #f4f6f9;
}
.course-card-content.expand
{
// Top shadow
// https://stackoverflow.com/questions/17572619/inset-box-shadow-only-on-one-side
box-shadow: inset 0 7px 9px -7px rgba(0,0,0,0.1);
}
+73
View File
@@ -0,0 +1,73 @@
<template>
<el-card id="course-card" class="course-card">
<course-head :clickable="false" :course="course" :unread="countUnread()"></course-head>
<div class="course-card-content expand">
<el-row>
<el-col :span="24">
<el-card class="large overall-line-card vertical-center">
<course-scatter :course="course"></course-scatter>
</el-card>
</el-col>
<el-col :span="0">
</el-col>
</el-row>
<AssignmentTypeHead v-for="type in getAssignmentTypes()" :key="type"
:type-name="type" :assignments="course.assignments">
</AssignmentTypeHead>
</div>
</el-card>
</template>
<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';
@Component({
components: {AssignmentEntry, CourseHead, CourseScatter, AssignmentTypeHead}
})
export default class CoursePage extends Vue
{
@Prop({required: true}) course: Course;
private unread: number = -1;
private unreadAssignments: Assignment[] = [];
/**
* Count the number of unread assignments with cache
*/
countUnread(): number
{
if (this.unread == -1)
{
this.unreadAssignments = this.course.assignments.filter(a => a.unread);
return this.unread = this.unreadAssignments.length;
}
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>
<style src="./course-page.scss" lang="scss" scoped></style>
@@ -0,0 +1,163 @@
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 moment from 'moment';
import Course from '@/logic/course';
@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;
/**
* Override options
*
* @param options Original options (Unused)
*/
afterConfig(options: any)
{
return this.chartSettings;
}
/**
* Generate settings
*/
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'
},
// X axis represents course names
xAxis:
{
type: 'time',
axisLabel:
{
formatter: (name: any) => moment(name).format('MMM DD')
},
max: FormatUtils.toChartDate(new Date())
},
// Y axis represents GPAs and MaxGPAs
yAxis:
{
type: 'value',
name: 'Percentage Score',
nameLocation: 'center',
nameGap: 38,
axisLabel:
{
formatter: (name: any) => name + '%'
},
max: 100,
min: (value: any) => Math.floor(value.min) - 5
},
// Tooltip
tooltip:
{
trigger: 'axis',
axisPointer:
{
type: 'cross'
},
formatter: (ps: any[]) => ps[0].data[0] + '<br>' + ps.map(p =>
`${CourseScatter.DOT.replace('{color}', p.color)}
${FormatUtils.limit(p.data[2], 22)}: ${p.data[1]}%<br>`).join('')
},
// Legend
legend:
{
bottom: 24,
itemWidth: 14,
textStyle:
{
color: '#777',
fontSize: 11
}
},
// Data
series: Array.from(map, ([type, assignments]) =>
{
return {
type: 'scatter',
name: type,
data: CourseScatter.assignmentsData(assignments),
itemStyle: itemStyle
}
})
};
return settings;
}
/**
* Map assignments to {assignmentType, [assignment]} format.
*/
private mapAssignments(): Map<string, Assignment[]>
{
// Define map
let map = new Map();
// Move data to map
this.course.assignments.forEach(a =>
{
// Null case, create empty array
if (!map.has(a.type)) map.set(a.type, []);
// Put data
map.get(a.type).push(a);
});
return map;
}
/**
* Convert assignments to series data
*
* @param assignments Assignments
*/
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]);
}
}
@@ -0,0 +1,16 @@
<template>
<div id="course-scatter">
<ve-scatter height="450px" class="graph" :extend="{heyIUsedCourseObject: this.course.name}" :after-config="afterConfig"></ve-scatter>
</div>
</template>
<script src="./course-scatter.ts" lang="ts"></script>
<style lang="scss" scoped>
#overall-bar
{
.graph
{
margin-top: 50px;
}
}
</style>
@@ -1,22 +0,0 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import {Course} from '@/components/app/app';
@Component({
})
export default class GraphOverall extends Vue
{
@Prop({required: true}) chart: any;
public chartData =
{
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 }
]
};
}
@@ -1,8 +0,0 @@
<template>
<div id="graph-overall">
<ve-line :data="chart" :extend="{series: {smooth: false}}"></ve-line>
</div>
</template>
<script src="./graph-overall.ts" lang="ts"></script>
<style src="./graph-overall.scss" lang="scss"></style>
@@ -0,0 +1,117 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import Course from '@/logic/course';
import {GPAUtils} from '@/utils/gpa-utils';
import Constants from '@/constants';
import {FormatUtils} from '@/utils/format-utils';
@Component({
})
export default class OverallBar extends Vue
{
@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) => FormatUtils.limit(value, 16)
},
},
// Y axis represents GPAs and MaxGPAs
yAxis:
{
type: 'value'
},
// Data
series:
[
// Max GP
{
type: 'bar',
barGap: '-100%',
data: this.courses.map(course =>
{
return {value: [course.name, GPAUtils.getGP(course, 'A+')], itemStyle: {color: '#d8d8d8'}}
}),
},
// Current GP
{
type: 'bar',
barGap: '-100%',
data: this.generateGPData(),
label:
{
show: true,
rotate: 90
}
}
],
// Disable tooltip
tooltip:
{
show: false
}
};
return settings;
}
/**
* Generate GP data for each course
*/
private generateGPData()
{
let data: any = [];
this.courses.forEach((course, index) =>
{
// Get GP
let gp = GPAUtils.getGP(course, course.letterGrade);
// No grade cases
if (gp == -1) return;
// Push data
data.push(
{
value: [course.name, gp],
itemStyle:
{
color: Constants.THEME.colors[index]
}
});
});
return data;
}
}
@@ -0,0 +1,17 @@
<template>
<div id="overall-bar">
<ve-bar height="450px" class="graph"
:extend="chartSettings"></ve-bar>
</div>
</template>
<script src="./overall-bar.ts" lang="ts"></script>
<style lang="scss" scoped>
#overall-bar
{
.graph
{
margin-top: 50px;
}
}
</style>
@@ -0,0 +1,142 @@
// Row
.assignment-entry
{
height: 40px;
padding: 0 10px 0 20px;
background: #f5f7fa;
text-align: left;
// Date
.el-col.date
{
min-width: 130px;
span.month
{
margin-right: 5px;
// Unified width
display: inline-block;
min-width: 50px;
}
span.now
{
font-size: 11px;
color: #888;
}
}
// Description
.el-col.description
{
width: unset;
span.type
{
display: inline-block;
font-size: 13px;
font-weight: 700;
background: #eee;
border-left: 2px solid #000;
height: 22px;
line-height: 22px;
margin-right: 8px;
}
}
// Grade
.el-col.grade
{
text-align: right;
float: right;
span.percent
{
font-style: italic;
background: #ffc;
color: #555;
margin-right: 8px;
.symbol
{
padding: 0 1px;
}
}
// Score you got
span.score
{
background: #f2f2f2;
color: #555;
}
// Max score
span.max
{
background: #ddd;
color: #333;
}
// Mark as read
button.mark-as-read
{
margin-left: 8px;
color: #aaa;
padding: 4px;
}
}
.entry-box
{
height: 22px;
padding: 0 5px;
}
// Unified width
.entry-box.score, .entry-box.max
{
min-width: 30px;
display: inline-block;
text-align: center;
}
// Unified width
.entry-box.percent
{
min-width: 60px;
display: inline-block;
text-align: right;
}
}
// Narrow layout
.assignment-entry.narrow
{
height: 34px;
}
// Unread
.no-unread
{
visibility: hidden !important;
width: 0 !important;
margin-left: 0 !important;
padding: 0 0 0 10px !important;
}
.assignment-entry:first-child
{
padding-top: 3px;
// Top shadow
// https://stackoverflow.com/questions/17572619/inset-box-shadow-only-on-one-side
box-shadow: inset 0 7px 9px -7px rgba(0,0,0,0.1);
}
@@ -0,0 +1,71 @@
<template>
<div class="assignment-entry vertical-center"
:class="narrow ? 'narrow' : ''"
:style="`background: ${backgroundColor}`">
<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>
</el-col>
<el-col :span="15" class="description">
<span class="type entry-box"
:style="`border-color: var(--assignment-type-${assignment.typeId})`">
{{assignment.type}}
</span>
<span class="text">{{assignment.description}}</span>
</el-col>
<el-col :span="6" class="grade">
<span class="percent entry-box">
{{(assignment.score / assignment.scoreMax * 100).toFixed(1)}}
<span class="symbol">%</span>
</span>
<span class="score entry-box">{{assignment.score}}</span>
<span class="max entry-box">{{assignment.scoreMax}}</span>
<el-button class="mark-as-read" :class="unread ? 'unread' : 'no-unread'"
size="mini" type="text" icon="el-icon-close"
@click="markAsRead">
</el-button>
</el-col>
</el-row>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
import {Assignment} from '@/components/app/app';
import moment from 'moment';
@Component
export default class AssignmentEntry extends Vue
{
@Prop({required: true}) assignment: Assignment;
@Prop({default: false}) unread: boolean;
@Prop({default: '#f5f7fa'}) backgroundColor: string;
@Prop({default: false}) narrow: boolean;
/**
* Format a date to the displayed format
*
* @param date Date
*/
getMoment(date: string)
{
return moment(new Date(date));
}
/**
* Mark this unread assignment as read
*/
markAsRead()
{
// Call custom event
this.$emit('mark-as-read', this.assignment)
}
}
</script>
<style src="./assignment-entry.scss" lang="scss" scoped></style>
@@ -0,0 +1,92 @@
// Main card content
.course-card-content.main
{
padding: 0 20px 0 20px;
height: 90px;
// Main color
background: white;
}
.course-col-name
{
// Align left
text-align: left;
float: left;
.course-name
{
overflow: hidden;
font-size: 22px;
color: var(--main);
}
.course-teacher
{
font-size: 12px;
color: #999999;
font-style: italic;
}
}
.course-col-grade
{
// Align right
text-align: right;
float: right;
// Adjust position
margin-top: -2px;
.course-grade
{
font-size: 21px;
}
.course-updates
{
font-size: 14px;
.unread-number
{
display: inline-block;
width: 20px;
text-align: center;
border-radius: 5px;
padding-left: 3px;
padding-right: 3px;
margin-right: 3px;
}
.unread-text
{
font-style: italic;
}
}
.course-updates.unread
{
.unread-number
{
background: var(--unread);
color: white;
}
.unread-text
{
color: var(--unread);
}
}
.course-updates.none
{
color: #999999;
.unread-number
{
background: #eeeeee;
}
}
}
@@ -0,0 +1,52 @@
<template>
<div id="course-head" class="course-card-content main vertical-center">
<el-row>
<el-col :span="12" class="course-col-name">
<div v-if="clickable" class="course-name clickable" @click="redirect">{{course.name}}</div>
<div v-if="!clickable" class="course-name">{{course.name}}</div>
<div class="course-teacher">{{course.teacherName}}</div>
</el-col>
<el-col :span="12" class="course-col-grade">
<div class="course-grade">
<span class="letter">{{course.letterGrade}} </span>
<span class="numeric">{{course.numericGrade === undefined ? '--' : course.numericGrade.toFixed(2)}}</span>
<span class="percent" v-if="course.numericGrade !== undefined">%</span>
</div>
<div class="course-updates" @click="redirect" :class="unread === 0 ? 'none' : 'unread'">
<span class="unread-number">{{unread}}</span>
<span class="unread-text" :class="clickable ? 'clickable' : ''">
new update{{unread >= 2 ? 's' : ''}}
</span>
</div>
</el-col>
</el-row>
</div>
</template>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
import Course from '@/logic/course';
import {CourseUtils} from '@/utils/course-utils';
import Navigation from '@/components/navigation/navigation';
@Component
export default class CourseHead extends Vue
{
@Prop({required: true}) unread: number;
@Prop({required: true}) course: Course;
@Prop({required: true}) clickable: boolean;
/**
* Redirect to the course page
*/
redirect()
{
if (!this.clickable) return;
Navigation.instance.updateIndex(CourseUtils.formatTabIndex(this.course));
}
}
</script>
<style src="./course-head.scss" lang="scss"></style>
@@ -0,0 +1,16 @@
// Card
.el-card.course-card
{
// Margins
margin-right: 20px;
margin-left: 20px;
// Height limit
max-height: 250px;
// Limit name length
white-space: nowrap;
// Expansion color
background: #f4f6f9;
}
@@ -0,0 +1,51 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import App, {Assignment} 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';
@Component({
components: {UnreadEntry: AssignmentEntry, CourseHead}
})
export default class OverallCourse extends Vue
{
@Prop({required: true}) course: Course;
private unread: number = -1;
private unreadAssignments: Assignment[] = [];
/**
* Count the number of unread assignments with cache
*/
countUnread(): number
{
if (this.unread == -1)
{
this.unreadAssignments = this.course.assignments.filter(a => a.unread);
return this.unread = this.unreadAssignments.length;
}
else return this.unread;
}
/**
* Mark an assignment as read
*/
markAsRead(assignment: Assignment)
{
App.http.post('/mark-as-read', {scoreId: assignment.scoreId})
.then(response =>
{
// Check success
if (response.success)
{
this.unreadAssignments = this.unreadAssignments.filter(a => a != assignment);
this.unread = this.unreadAssignments.length;
}
else
{
// Show error message TODO: Show it properly
alert(response.data)
}
})
}
}
@@ -0,0 +1,19 @@
<template>
<div id="overall-course">
<el-card class="course-card">
<course-head :clickable="true" :course="course" :unread="countUnread()"></course-head>
<div class="course-card-content expand"
v-if="countUnread() !== 0">
<unread-entry v-for="assignment in unreadAssignments"
:assignment="assignment"
:key="assignment.id"
unread="true"
v-on:mark-as-read="markAsRead">
</unread-entry>
</div>
</el-card>
</div>
</template>
<script src="./overall-course.ts" lang="ts"></script>
<style src="./overall-course.scss" lang="scss" scoped></style>
@@ -0,0 +1,224 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import moment from 'moment';
import Course from '@/logic/course';
@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;
/**
* Convert assignments list to a graph dataset.
*/
get convertChart()
{
// Caching
if (this.chartCache != undefined) return this.chartCache;
let courses = this.courses.filter(c => c.assignments.length > 0);
// Compute the column names
let columns = courses.map(course => course.name);
columns.unshift('date');
// Find the min date
let minDates = courses.map(course => 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
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);
});
return this.chartCache =
{
columns: columns,
rows: rows
}
}
}
@@ -0,0 +1,10 @@
<template>
<div id="overall-line">
<ve-line :data="convertChart" :extend="settings"></ve-line>
</div>
</template>
<script src="./overall-line.ts" lang="ts"></script>
<style lang="scss" scoped>
</style>
+39 -15
View File
@@ -1,29 +1,53 @@
// Add some margins
.el-card
.gpa-card
{
margin: 10px;
height: 494px;
padding: 0;
// Vertical center
display: flex;
justify-content: center;
flex-direction: column;
margin-left: 20px;
min-width: 136px;
}
.span-gpa-header
.gpa
{
display: block;
}
.gpa.header
{
font-size: 14px;
}
.span-gpa
.gpa.text
{
font-size: 35px;
font-family: 'Avenir', Helvetica, Arial, sans-serif;
font-family: var(--font);
}
.gpa-time
.gpa.max
{
font-size: 14px;
margin-top: -10px;
margin-bottom: 10px;
font-size: 12px;
color: #409eff;
}
.gpa.time
{
font-size: 11px;
}
.no-grade
{
font-size: 30px;
color: #b1b1b1;
// Disable selecting
display:block;
pointer-events: none;
user-select: none;
}
// Cards
.el-card.overall-bar-card
{
margin-right: 20px;
min-width: 170px;
}
-126
View File
@@ -1,126 +0,0 @@
import {Component, Prop, Vue} from 'vue-property-decorator';
import GraphOverall from '@/pages/overall/graph-overall/graph-overall';
import {Course} from '@/components/app/app';
import {GPAUtils} from '@/utils/gpa-utils';
@Component({
components: {GraphOverall}
})
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
}
}
public getGPA()
{
let gpa = GPAUtils.getGPA(this.courses);
let result = '' + gpa.gpa;
/* Not accurate
if (!gpa.accurate)
{
result = `(${result})`;
}*/
return result;
}
}
+53 -16
View File
@@ -1,30 +1,67 @@
<template>
<div id="overall">
<el-row>
<el-row v-if="getGPA().gpa !== -1">
<el-col :span="4">
<el-card style="margin-left: 20px">
<el-card class="large gpa-card vertical-center">
<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 class="large overall-line-card vertical-center">
<overall-line :courses="courses"></overall-line>
</el-card>
</el-col>
<el-col :span="6">
<el-card class="large overall-bar-card vertical-center">
<overall-bar :courses="courses"></overall-bar>
</el-card>
</el-col>
</el-row>
<div class=""></div>
<el-row v-if="getGPA().gpa === -1">
<el-card class="large gpa-card vertical-center">
<div class="no-grade">This quarter has no grades yet...</div>
</el-card>
</el-row>
<overall-course v-for="course in courses"
:course="course"
:key="course.id">
</overall-course>
</div>
</template>
<script src="./overall.ts" lang="ts"></script>
<style src="./overall.scss" lang="scss"></style>
<script lang="ts">
import {Component, Prop, Vue} from 'vue-property-decorator';
import OverallLine from '@/pages/overall/overall-line/overall-line';
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';
@Component({
components: {OverallLine, OverallBar, OverallCourse}
})
export default class Overall extends Vue
{
@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()
{
return GPAUtils.getGPA(this.courses);
}
}
</script>
<style src="./overall.scss" lang="scss" scoped></style>
+1 -1
View File
@@ -1,4 +1,4 @@
import Vue, { VNode } from 'vue';
import Vue, {VNode} from 'vue';
declare global {
namespace JSX {
+4 -3
View File
@@ -1,4 +1,5 @@
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
declare module '*.vue'
{
import Vue from 'vue';
export default Vue;
}
+67
View File
@@ -0,0 +1,67 @@
import Course from '@/logic/course';
const LEVEL_AP = {level: 'AP', scaleUp: 1};
const LEVEL_H = {level: 'H', scaleUp: 0.75};
const LEVEL_A = {level: 'A', scaleUp: 0.5};
const LEVEL_CP = {level: 'CP', scaleUp: 0.25};
const LEVEL_CLUB = {level: 'Club', scaleUp: -1};
const UNKNOWN_COURSE_LIST = new Map();
UNKNOWN_COURSE_LIST.set('Piano Masterclass', LEVEL_H);
UNKNOWN_COURSE_LIST.set('Multivariable Calculus with Differential Equations', LEVEL_H);
UNKNOWN_COURSE_LIST.set('Introduction to Algorithmic Thinking and Computational Technologies', LEVEL_A);
UNKNOWN_COURSE_LIST.set('Ceramics 1', LEVEL_CP);
UNKNOWN_COURSE_LIST.set('Ceramics 2', LEVEL_A);
UNKNOWN_COURSE_LIST.set('Sculpture', LEVEL_CP);
UNKNOWN_COURSE_LIST.set('Drawing', LEVEL_CP);
UNKNOWN_COURSE_LIST.set('Painting', LEVEL_CP);
export class CourseUtils
{
/**
* Format course to tab index string
*
* @param course Course object
* @return string Tab index
*/
public static formatTabIndex(course: Course): string
{
return `course/${course.id}/${course.name.toLowerCase().split(' ').join('-')}`;
}
/**
* Detect course level based on course name
*
* @param name Course name
*/
static detectLevel(name: string)
{
// Common ones
if (name.startsWith('AP')) return LEVEL_AP;
if (name.endsWith(' H')) return LEVEL_H;
if (name.endsWith(' A')) return LEVEL_A;
if (name.endsWith(' CP')) return LEVEL_CP;
if (name.startsWith('HS ')) return LEVEL_CLUB;
if (name.startsWith('MS ')) return LEVEL_CLUB;
// Uncommon ones
let lower = name.toLowerCase();
if (name.startsWith('Pre-AP')) return LEVEL_AP;
if (lower.endsWith(' acc')) return LEVEL_A;
if (name.endsWith('H')) return LEVEL_H;
if (name.endsWith('A')) return LEVEL_A;
if (name.endsWith('CP')) return LEVEL_CP;
// Even more uncommon
if (lower.includes('honors')) return LEVEL_H;
if (lower.includes('accelerated')) return LEVEL_A;
if (name.includes('Advanced')) return LEVEL_A;
// Unknown course list
if (UNKNOWN_COURSE_LIST.has(name)) return UNKNOWN_COURSE_LIST.get(name);
// Really unknown
return undefined;
}
}
+50
View File
@@ -0,0 +1,50 @@
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
*
* @param str String
* @param length Max length
*/
public static limit(str: string, length: number): string
{
return str.length <= length ? str : str.substr(0, length - 2) + '...'
}
/**
* To Title Case
*
* @param str oRigInAL sTrING
* @return string Original String
*/
public static toTitleCase(str: string)
{
return str.replace(/\w\S*/g, s => s.charAt(0).toUpperCase() + s.substr(1).toLowerCase())
}
/**
* Parse html text
*
* @param str
*/
public static parseText(str: string): string
{
return str.replace(/&amp;/g, '&');
}
}
+128 -38
View File
@@ -1,37 +1,40 @@
import Course from '@/logic/course';
import {Assignment} from '@/components/app/app';
export interface Scale
{
min: number
letter: string
gp: number
}
/**
* This is an utility class to calculate GPA.
*/
import {Course} from '@/components/app/app';
export class GPAUtils
{
// [[Min score, Letter grade, Base GPA], ...]
public static SCALE =
public static SCALE: Scale[] =
[
[96.5, 'A+', 4.00],
[92.5, 'A' , 3.75],
[89.5, 'A-', 3.50],
[86.5, 'B+', 3.25],
[82.5, 'B' , 3.00],
[79.5, 'B-', 2.75],
[76.5, 'C+', 2.50],
[72.5, 'C' , 2.25],
[70.5, 'C-', 2.00],
[69.5, 'D' , 1.00],
[0 , 'F' , 0.00]
{min: 96.5, letter: 'A+', gp: 4.00},
{min: 92.5, letter: 'A' , gp: 3.75},
{min: 89.5, letter: 'A-', gp: 3.50},
{min: 86.5, letter: 'B+', gp: 3.25},
{min: 82.5, letter: 'B' , gp: 3.00},
{min: 79.5, letter: 'B-', gp: 2.75},
{min: 76.5, letter: 'C+', gp: 2.50},
{min: 72.5, letter: 'C' , gp: 2.25},
{min: 70.5, letter: 'C-', gp: 2.00},
{min: 69.5, letter: 'D' , gp: 1.00},
{min: 0 , letter: 'F' , gp: 0.00}
];
// Keywords
public static MIN = 0;
public static LETTER = 1;
public static GPA = 2;
/**
* Calculate GPA for a list of couses
*
* @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 +45,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,47 +58,134 @@ 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)
// Get scale
let scale = this.findScale(letterGrade);
// No scale
if (scale == undefined) return -1;
// Add scaleUp if not failed.
return scale.gp == 0 ? 0 : scale.gp + course.scaleUp;
}
/**
* Find the scale for a grade
*
* @param grade Letter grade or numeric grade
*/
public static findScale(grade: string | number): Scale | undefined
{
// Letter grade
if (typeof grade == 'string')
{
// Letter grades are the same
if (scale[this.LETTER] == course.letterGrade)
return this.SCALE.find(scale => scale.letter == grade);
}
// Numeric grade
return this.SCALE.find(scale => grade >= scale.min);
}
/**
* Calculate the total-mean (total/max) average
*
* @param assignments
*/
public static getTotalMeanAverage(assignments: Assignment[])
{
let score = 0;
let max = 0;
// Loop through assignments
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;
}
/**
* Calculate the percent type
* TODO: Combine it with overall-line
*
* @param course
* @param assignments
*/
public static getPercentTypeAverage(course: Course, assignments: Assignment[])
{
let typeScores: {[index: string]: any} = {};
let typeCounts: {[index: string]: any} = {};
// Loop through assignments
assignments.forEach(assignment =>
{
// If assignment should be displayed
if (assignment.complete != 'Complete') return;
// 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)
{
// Get grade and add it
let grade = <number> scale[this.GPA];
// Add scaleUp if not failed.
if (grade != 0) grade += course.scaleUp;
// That's it
return grade;
totalPercentage += course.grading.weightingMap[type];
}
}
return -1;
// 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
return score * 100;
}
}
-5
View File
@@ -4,11 +4,6 @@ export class HttpUtils
{
public token: string = '';
constructor (token: string)
{
this.token = token;
}
public post(node: string, body: any): Promise<any>
{
// Add token
+17 -15
View File
@@ -1,4 +1,4 @@
import {Grade} from '@/components/app/app';
import {Assignment} from '@/components/app/app';
export default class JsonUtils
{
@@ -6,28 +6,30 @@ 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
* @returns Assignment[] Filtered assignment grade object list
*/
public static filterAssignments(assignments: any): Grade[]
public static filterAssignments(assignments: any): Assignment[]
{
let result: Grade[] = [];
assignments.assignments.forEach((assignment: any) =>
return assignments.assignments.map((assignment: any) =>
{
result.push(
{
return {
id: assignment.assignment_id,
scoreId: assignment.score_id,
type: assignment.assignment_type,
typeId: assignment.assignment_type_id,
description: assignment.assignment_description,
date: assignment._date,
complete: assignment.complete_status,
date: new Date(assignment._date),
complete: assignment.completion_status,
include: assignment.include_in_calculated_grade == 1,
display: assignment.display_grade == 1,
scoreMax: assignment.maximum_score,
score: +assignment.raw_score
});
});
unread: assignment.is_unread == 1,
return result;
scoreMax: assignment.maximum_score,
score: +assignment.raw_score,
gradingPeriod: +assignment.grading_period.replace('Quarter ', '') - 1
}
});
}
}
+41
View File
@@ -0,0 +1,41 @@
export default class VersionUtils
{
/**
* Compare two version numbers
*
* Eg.
* compare('0.1.2', '0.1.3') = -1
* compare('1.0.0', '0.1.3') = 1
* compare('0.0.1', '0.0.1') = 0
*
* @param ver1 Version 1
* @param ver2 Version 2
* @return number (-1 if ver1 < ver2), (1 if ver1 > ver2), (0 if equal)
*/
public static compare(ver1: string, ver2: string): number
{
// Equal case
if (ver1 == ver2) return 0;
// Split
let split1 = ver1.split('.');
let split2 = ver2.split('.');
// Detect each number
for (let i in split1)
{
// Get numbers
let num1 = split1[i];
let num2 = split2[i];
// Current number is equal
if (num1 == num2) continue;
// Current number is different
return +num1 < +num2 ? -1 : 1;
}
// Equal
return 0;
}
}
+4 -1
View File
@@ -24,7 +24,10 @@
"dom",
"dom.iterable",
"scripthost"
]
],
// Custom
"strictPropertyInitialization": false
},
"include": [
"src/**/*.ts",
+5 -3
View File
@@ -1,5 +1,7 @@
module.exports = {
devServer: {
module.exports =
{
devServer:
{
disableHostCheck: true,
}
}
};