Completed
Push — master ( 5e0f69...985619 )
by Equim
01:05
created
1
'use strict';
2
3
const
4
    express    = require('express'),
5
    superagent = require('superagent'),
6
    cheerio    = require('cheerio'),
7
    colors     = require('colors'),
0 ignored issues
show
The constant colors seems to be never used. Consider removing it.
Loading history...
8
    program    = require('commander'),
9
    moment     = require('moment'),
10
    co         = require('co'),
11
    thunkify   = require('thunkify'),
12
    access     = require('./lib/access.js');
13
14
program
15
    .option('-h, --help')
16
    .option('-v, --version')
17
    .option('-p, --port [value]', parseInt)
0 ignored issues
show
The variable parseInt seems to be never declared. If this is a global, consider adding a /** global: parseInt */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
18
    .option('-f, --fullLog')
19
    .parse(process.argv);
20
21
// 别问我为什么这里逻辑这么奇怪……测试的结果确实是这样的啊hhh
22
if (!program.help || !program.version) {
23
    console.log(
24
`${`CSUEMS API v${require('./package').version}
25
by The Liberators`.rainbow}${program.help ? '' :
26
`
27
28
Preparation:
29
  ${'(This section is WIP)'.grey}
30
31
Usage:
32
  npm start [-- <options...>]
33
34
Options:
35
  -h, --help          print this message and exit.
36
  -v, --version       print the version and exit.
37
  -f, --fullLog       enable full log, by default only errors are logged.
38
  -p, --port [value]  specify a port to listen, process.env.PORT || 2333 by default.
39
40
Examples:
41
  $ npm start -- -p 43715               # listening to 43715
42
  $ forever start app.js                # deploy with forever as daemon (root access recommended)
43
  $ pm2 start -i 0 -n "csuapi" app.js   # deploy with pm2 as daemon  (root access recommended)`}`);
44
    process.exit(0);
45
}
46
47
superagent.Request.prototype.endThunk = thunkify(superagent.Request.prototype.end);
48
49
const
50
    logging = log => console.log(`${moment().format('[[]YY-MM-DD HH:mm:ss[]]')} ${log}`),
51
    fullLogging = log => program.fullLog && logging(log),
52
    getSem = () => {
53
        // 以系统时间获取当前学期
54
        let now = new Date();
55
        let month = now.getMonth();
56
        let year = now.getFullYear();
57
        if (month <= 6) {
58
            return `${year - 1}-${year}-${month > 0 ? 2 : 1}`;
59
        } else {
60
            return `${year}-${year + 1}-1`;
61
        }
62
    },
63
//  wait = ms => callback => setTimeout(callback, ms),
64
    port = program.port || process.env.PORT || 2333,
65
    app = express();
66
67
// 获取文档
68
app.get('/doc', (req, res) => res.sendFile(`${__dirname}/doc/API.html`));
69
70
// 查成绩API,通过GET传入用户名和密码
71 View Code Duplication
app.get(/^\/g(?:|rades)$/, co.wrap(function* (req, res) {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
72
    if (!req.query.id || !req.query.pwd || (req.query.sem && !(/^20\d{2}-20\d{2}-[1-2]$/).test(req.query.sem))) {
73
        res.status(404).send({ error: "参数不正确" });
74
        return;
75
    }
76
77
    let start = new Date();
78
    fullLogging('Started to query the grades: '.cyan + req.query.id.yellow);
79
80
    let headers;
81
    try {
82
        headers = yield access.login(req.query.id, req.query.pwd, res);
83
    } catch (err) { return; }
84
    fullLogging('Successfully logged in.'.green);
85
86
    let ires;
87
    try {
88
        // 实际上xnxq01id为空的时候和GET这个URL的效果是一样的,都是查询所有学期
89
        ires = yield superagent
90
            .post('http://csujwc.its.csu.edu.cn/jsxsd/kscj/yscjcx_list')
91
            .set(headers)
92
            .type('form')
93
            .send({ xnxq01id: req.query.sem })
94
            .endThunk();
95
    } catch (err) {
96
        logging(` Failed to get grades page\n${err.stack}`.red);
97
        res.status(404).send({ error: '无法进入成绩页面' });
98
        return;
99
    } finally {
100
        // 直接异步进行?
101
        co(function* () {
102
            yield access.logout(headers);
103
            fullLogging('Successfully logged out: '.green + req.query.id.yellow);
104
        });
105
    }
106
    fullLogging('Successfully entered grades page.'.green);
107
108
    let $ = cheerio.load(ires.text);
109
110
    let top = $('#Top1_divLoginName').text();
111
    let result = {
112
        name: top.match(/\s.+\(/)[0].replace(/\s|\(/g, ''),
113
        id: top.match(/\(.+\)/)[0].replace(/\(|\)/g, ''),
114
        grades: {},
115
        'subject-count': 0,
116
        failed: {},
117
        'failed-count': 0,
118
    };
119
    // 获取成绩列表
120
    $('#dataList tr').each(function (index) {
121
        if (index === 0) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
122
123
        let element = $(this).find('td');
124
125
        let title = element.eq(3).text().match(/].+$/)[0].substring(1);
126
        let item = {
127
            sem: element.eq(2).text(),
128
            reg: element.eq(4).text(),
129
            exam: element.eq(5).text(),
130
            overall: element.eq(6).text()
131
        };
132
        if (req.query.details) {
133
            item.id = element.eq(3).text().match(/\[.+\]/)[0].replace(/\[|\]/g, '');
134
            item.attr = element.eq(8).text();
135
            item.genre = element.eq(9).text();
136
            item.credit = element.eq(7).text();
137
        }
138
139
        // 如果有补考记录,则以最高分的为准
140
        // 这段代码是拿可读性换了时间复杂度……
141
        if (title in result.grades) {
142
            // 暂不考虑NaN
143
            if (item.overall < result.grades[title].overall) {
144
                return;
145
            }
146
            // 如果新的成绩不是挂科的
147
            if (!element.eq(6).css('color')) {
148
                delete result.failed[title];
149
            }
150
        } else if (element.eq(6).css('color')) {
151
            result.failed[title] = item;
152
        }
153
154
        result.grades[title] = item;
155
    });
156
157
    result['subject-count'] = Object.keys(result.grades).length;
158
    result['failed-count'] = Object.keys(result.failed).length;
159
160
    res.send(JSON.stringify(result));
161
    fullLogging(`Successfully responded. (req -> res processed in ${new Date() - start} ms)`.green);
162
}));
163
164
// 查考试API,通过GET传入用户名和密码
165 View Code Duplication
app.get(/^\/e(?:|xams)$/, co.wrap(function* (req, res) {
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
166
    if (!req.query.id || !req.query.pwd || (req.query.sem && !(/^20\d{2}-20\d{2}-[1-2]$/).test(req.query.sem))) {
167
        res.status(404).send({ error: "参数不正确" });
168
        return;
169
    }
170
171
    let start = new Date();
172
    fullLogging('Started to query the exams: '.cyan + req.query.id.yellow);
173
174
    let headers;
175
    try {
176
        headers = yield access.login(req.query.id, req.query.pwd, res);
177
    } catch (err) { return; }
178
    fullLogging('Successfully logged in.'.green);
179
180
    let ires;
181
    let _sem = req.query.sem || getSem();
182
    try {
183
        ires = yield superagent
184
            .post('http://csujwc.its.csu.edu.cn/jsxsd/xsks/xsksap_list')
185
            .set(headers)
186
            .type('form')
187
            .send({
188
                xqlbmc: '',
189
                xnxqid: _sem,
190
                xqlb: ''
191
            })
192
            .endThunk();
193
    } catch (err) {
194
        logging(` Failed to reach exams page\n${err.stack}`.red);
195
        res.status(404).send({ error: '无法进入考试页面' });
196
        return;
197
    } finally {
198
        co(function* () {
199
            yield access.logout(headers);
200
            fullLogging('Successfully logged out: '.green + req.query.id.yellow);
201
        });
202
    }
203
    fullLogging('Successfully entered exams page.'.green);
204
205
    let $ = cheerio.load(ires.text);
206
207
    let top = $('#Top1_divLoginName').text();
208
    let result = {
209
        name: top.match(/\s.+\(/)[0].replace(/\s|\(/g, ''),
210
        id: top.match(/\(.+\)/)[0].replace(/\(|\)/g, ''),
211
        sem: _sem,
212
        exams: {},
213
        'exams-count': 0,
214
    };
215
    $('#dataList tr').each(function (index) {
216
        if (index === 0) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
217
218
        let element = $(this).find('td');
219
220
        let title = element.eq(3).text();
221
        let item = {
222
            time: element.eq(4).text(),
223
            location: element.eq(5).text(),
224
            seat: element.eq(6).text()
225
        };
226
227
        result.exams[title] = item;
228
        result['exams-count']++;
229
    });
230
231
    res.send(JSON.stringify(result));
232
    fullLogging(`Successfully responded. (req -> res processed in ${new Date() - start} ms)`.green);
233
}));
234
235
app.listen(port, () => {
236
    logging(` The API is now running on port ${port}. Full logging is ${program.fullLog ? 'enabled' : 'disabled'}`.green);
237
});