-
Notifications
You must be signed in to change notification settings - Fork 0
/
gradeUtil_for16.js
230 lines (201 loc) · 6.97 KB
/
gradeUtil_for16.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
const fs = require('fs')
const path = require('path')
const PASS = 'pass'
const FAIL = 'fail'
const EXP = 'expected'
// 標準 <http://www.gakuji.keio.ac.jp/academic/shoumei/grading_system.html>
const gradeTbl16 = { 'A': 4, 'B': 3, 'C': 2, 'D': 0, '★': FAIL, 'P': PASS, 'F': FAIL, 'G': PASS, '?': EXP, '-': EXP}
const gradeTbl17 = {'S': 4, 'A': 3, 'B': 2, 'C': 1, 'D': 0, 'P': PASS, 'F': FAIL, 'G': PASS, '?': EXP, '-': EXP}
// // 交換留学用 <http://www.ic.keio.ac.jp/keio_student/exchange/qualifications.html>
// const gradeTbl16 = { 'A': 5, 'B': 4, 'C': 3, 'D': 0, '★': FAIL, 'P': PASS, 'F': FAIL, 'G': PASS, '?': EXP, '-': EXP}
// const gradeTbl17 = {'S': 5, 'A': 4.5, 'B': 4, 'C': 3, 'D': 0, 'P': PASS, 'F': FAIL, 'G': PASS, '?': EXP, '-': EXP}
// // 奨学金用 (JASSO用成績係数) <http://www.ic.keio.ac.jp/keio_student/scholarship/2018jasso_1.2.ad.html>
// const gradeTbl16 = { 'A': 3, 'B': 2, 'C': 1, 'D': 0, '★': FAIL, 'P': PASS, 'F': FAIL, 'G': PASS, '?': EXP, '-': EXP}
// const gradeTbl17 = {'S': 3, 'A': 3, 'B': 2, 'C': 1, 'D': 0, 'P': PASS, 'F': FAIL, 'G': PASS, '?': EXP, '-': EXP}
const indexTbl = {
className: 0,
profName: 1,
grade: 2,
credit: 3,
makeup: 4,
year: 5,
term: 6,
when: 7,
}
;(function () {
const argv = process.argv
if (argv.length !== 3) {
console.log(`usage: node ${path.basename(argv[1])} fileName`)
return
}
readFile(argv[2])
.then(read => parseFile(read))
.then(parsed => calcStat(parsed))
.then(res => formatInfo(res))
.catch(err => print(err))
})()
function readFile (filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
// parseFile :: String fileData -> {'header': [String header ...],
// 'List': {String category: [[String className ...] ...] ...}}
function parseFile (fileData) {
return new Promise ((resolve, reject) => {
let Parsed = {}
let table = fileData.split('\n').map(line =>
line.split('\t').map(e => e.trim()))
Parsed.header = table.shift()
Parsed.List = {}
let category = ''
table.forEach(row => {
if (row.length === 2) {
category = row[0].slice(12)
Parsed.List[category] = []
} else if (row.length === 8) {
Parsed.List[category].push(row)
}
})
resolve(Parsed)
})
}
// calcStat :: Object Parsed -> +{'Stat': {sum_credit ...},
// 'List': {'Stat': {Number credit, Number grade ...} ...}
function calcStat (Parsed) {
Parsed.Stat = {}
let Cat = {}
let Type = {}
let sum_credit = 0
let sum_grade = 0
let sum_failed = 0
let sum_pending = 0
let sum_creditExtra = 0
let sum_gradeExtra = 0
let sum_failedExtra = 0
let sum_pendingExtra = 0
let sum_deduct = 0 // deduct PASS/FAIL credits from GPA calculation
Object.keys(Parsed.List).forEach(category => {
let credit = 0
let grade = 0
let failed = 0
let pending = 0
let deduct = 0
Parsed.List[category].forEach(row => {
let gradeTbl = (Number(row[indexTbl.year]) < 2017) ? gradeTbl16 : gradeTbl17
let c = Number(row[indexTbl.credit])
let g = gradeTbl[row[indexTbl.grade]]
switch (g) {
case PASS: credit += c
deduct += c; break
case FAIL: failed += c
deduct += c; break
case EXP: pending += c; break
default:
grade += g * c
if (g === 0) failed += c
else credit += c
}
})
// 自由科目の単位は卒業単位には含まれない
if (category.includes('自由科目')) {
sum_creditExtra += credit
sum_gradeExtra += grade
sum_failedExtra += failed
sum_pendingExtra += pending
} else {
sum_credit += credit
sum_grade += grade
sum_failed += failed
sum_pending += pending
sum_deduct += deduct
}
Cat[category] = {
credit,
grade,
failed,
pending,
gpa: grade / (credit + failed - deduct),
}
})
Type = gatherType(Cat)
Parsed.Stat = {
sum_credit,
sum_grade,
sum_failed,
sum_pending,
sum_creditExtra,
sum_gradeExtra,
sum_failedExtra,
sum_pendingExtra,
// 16年以前の入学者は成績表にDは記載されないため、落とした科目はGPA計算に含まない
sum_creditWithExtra: sum_credit + sum_creditExtra,
gpa: sum_grade / (sum_credit /* + sum_failed*/ - sum_deduct),
gpaWithExtra: (sum_grade + sum_gradeExtra) / (sum_credit /*+ sum_failed */ + sum_creditExtra /*+ sum_failedExtra*/ - sum_deduct),
Type,
Cat,
}
return Parsed
}
// gatherType :: Object Categories -> {String type: {joinedStat} ...}
function gatherType (Cat) {
Type = {}
Object.keys(Cat).forEach(category => {
let prefix = category.split(' ')[0]
if (Type[prefix]) Type[prefix] = joinObj(Type[prefix], Cat[category])
else Type[prefix] = Cat[category]
})
Object.keys(Type).forEach(t => {
Type[t].gpa = Type[t].grade / Type[t].credit
})
return Type
}
function joinObj (A, B) {
R = {}
Object.keys(A).forEach(keyA => {
Object.keys(B).forEach(keyB => {
if (keyA === keyB)
R[keyA] = A[keyA] + B[keyB]
})
})
return R
}
// formatInfo :: Object Record -> IO
function formatInfo (Record) {
// http://www.gakuji.keio.ac.jp/sfc/pe/3946mc0000022u8x-att/3946mc0000022vc8.pdf
const req_senior_basic = 30
const req_graduate_advanced = 30
const req_graduate_total = 124
let S = Record.Stat
info = `現在の取得単位: ${S.sum_credit}
落とした単位: ${S.sum_failed}
GPA: ${S.gpa.toFixed(2)}
取得予定の単位: ${S.sum_pending}
--------------------------
4年進級条件:
基盤科目: ${S.Type['基盤科目'] ?
S.Type['基盤科目'].credit : 0}/${req_senior_basic}
体育2・3: ${S.Cat['基盤科目 ウェルネス科目 体育2・3'] ?
S.Cat['基盤科目 ウェルネス科目 体育2・3'].credit : 0}/2
研究会: ${S.Cat['研究プロジェクト科目 研究会'] ?
S.Cat['研究プロジェクト科目 研究会'].credit : 0}/2
卒業条件:
先端科目: ${S.Type['先端科目'] ?
S.Type['先端科目'].credit : 0}/${req_graduate_advanced}
合計: ${S.sum_credit}/${req_graduate_total} ${(S.sum_credit < req_graduate_total) ?
['(あと', req_graduate_total - S.sum_credit, '単位)'].join('') : ''}
--------------------------
自由科目の情報:
取得単位: ${S.sum_creditExtra} (合計${S.sum_creditWithExtra})
落とした単位: ${S.sum_failedExtra}
自由科目込みのGPA: ${S.gpaWithExtra.toFixed(2)}
取得予定の単位: ${S.sum_pendingExtra}
`
console.log(info)
}
function print (data) {
console.log(JSON.stringify(data, null, 4))
}