1、前言
這個作業屬於哪個課程 | 班級鏈接 |
這個作業要求在哪里 | 作業要求 |
這個作業的目標 | 使用github、規范代碼 |
作業正文 | 作業正文 |
其他參考文獻 | 暫無 |
2、PSP
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 10 | 10 |
Estimate | 估計這個任務需要多少時間 | 490 | 470 |
Development | 開發 | 240 | 210 |
Analysis | 需求分析 (包括學習新技術) | 30 | 30 |
Design Spec | 生成設計文檔 | 10 | 10 |
Design Review | 設計復審 | 10 | 10 |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 20 | 15 |
Design | 具體設計 | 10 | 10 |
Coding | 具體編碼 | 240 | 210 |
Code Review | 代碼復審 | 30 | 40 |
Test | 測試(自我測試,修改代碼,提交修改) | 470 | 470 |
Reporting | 報告 | 10 | 10 |
Test Repor | 測試報告 | 10 | 10 |
Size Measurement | 計算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 20 | 20 |
合計 | 890 | 870 |
3、思路描述

4、實現過程

5、代碼說明
這里是代碼地址,有詳細注釋
命令行參數與統計結果的數據結構如下:
代碼僅由以下三部分構成,共130余行:
1.獲得命令行參數的代碼:
if(argv[0]!='list') throw new Error('僅能接受list命令!')
argv.slice(1).forEach(v => {
var checked = false //checked為true,說明該參數是一個以-為前綴的key
for (item of Object.keys(CmdParam))
if (v == '-' + item) {
reading = item
checked = true
break
}
!checked && CmdParam[reading].push(v) //統計鍵值對
})
if(!fs.existsSync(CmdParam.log[0])) throw new Error('輸入的日志目錄不存在!')
if (fs.readdirSync(CmdParam.log[0]).sort().reverse()[0].split('.')[0] < CmdParam.date[0])
throw new Error('日期超出范圍!(-date不會提供在日志最晚一天后的日期)');
if (!CmdParam.date[0])
return fs.readdirSync(CmdParam.log[0]).sort().reverse()[0].split('.')[0].split('-') //最大日期
else //指定了日期
return CmdParam.date[0].split('-')
2.匹配文件內容的代碼:
data = data.split('\n') //分行,data是單個文件內的全部數據
data.forEach((line) => {
var res = []
function match(reg, hasTwoPvovinces) { //匹配正則,清洗數據以及初始化
if (/\/\//.test(line)) return false //如果是注釋,忽略掉
var res = reg.exec(line)
if (!res) return
for (key of Object.keys(Total)) { //初始化數據數字為Number類型,防止NaN
if (!Total[key][res[1]]) { //非undefined類型代表已經初始化過
provinces.add(res[1]) //順便收集唯一省份
Total[key][res[1]] = 0
}
if (hasTwoPvovinces && !Total[key][res[2]]) { //同上,並且判斷要收集第三項數據
provinces.add(res[2]) //順便收集唯一省份
Total[key][res[2]] = 0
}
}
return res.slice(1, 4) //返回正則匹配的組內容
}
if (res = match(/(\S{2,3})\s新增 感染患者 (\d*)人/g)) //正則並收集數據
Total.ip[res[0]] += Number(res[1])
if (res = match(/(\S{2,3})\s新增 疑似患者 (\d*)人/g))
Total.sp[res[0]] += Number(res[1])
if (res = match(/(\S{2,3})\s疑似患者 流入 (\S{2,3})\s(\d*)人/g, true)) {
Total.sp[res[0]] -= Number(res[2])
Total.sp[res[1]] += Number(res[2])
}
if (res = match(/(\S{2,3})\s感染患者 流入 (\S{2,3})\s(\d*)人/g, true)) {
Total.ip[res[0]] -= Number(res[2])
Total.ip[res[1]] += Number(res[2])
}
if (res = match(/(\S{2,3})\s死亡 (\d*)人/g)) {
Total.ip[res[0]] -= Number(res[1])
Total.dead[res[0]] += Number(res[1])
}
if (res = match(/(\S{2,3})\s治愈 (\d*)人/g)) {
Total.cure[res[0]] += Number(res[1])
Total.ip[res[0]] -= Number(res[1])
}
if (res = match(/(\S{2,3})\s疑似患者 確診感染 (\d*)人/g)) {
Total.ip[res[0]] += Number(res[1])
Total.sp[res[0]] -= Number(res[1])
}
if (res = match(/(\S{2,3})\s排除 疑似患者 (\d*)人/g))
Total.sp[res[0]] -= Number(res[1])
})
3.整理數據並輸出的代碼:
var provincesSorted = []
for (let item of Provinces.keys()) { //提取集合里的省份
provincesSorted.push(item)
}
provincesSorted = provincesSorted.sort((a, b) => { //進行漢字拼音排序
return prior.indexOf(a) - prior.indexOf(b)
})
for (let item of Object.keys(Total)) { //統計'全國'的數據
let sum = 0
for (let num of Object.values(Total[item]))
sum += num
Total[item]['全國'] = sum
}
provincesSorted.unshift('全國') //確保‘全國’一定在其他省份的前面
if (!CmdParam.type.length) CmdParam.type = ['ip', 'sp', 'cure', 'dead'] // 不指定-type即為輸出全部四項
provincesSorted.forEach((v) => {
if (!(CmdParam.province.length == 0 || CmdParam.province.includes(v))) return //篩選-province省份
article += `${v}` //開始處理輸出數據的附加項-type
if (CmdParam.type.includes('ip')) article += ` 感染患者${Total.ip[v]}人`
if (CmdParam.type.includes('sp')) article += ` 疑似患者${Total.sp[v]}人`
if (CmdParam.type.includes('cure')) article += ` 治愈${Total.cure[v]}人`
if (CmdParam.type.includes('dead')) article += ` 死亡${Total.dead[v]}人`
article += `\n`
})
article += `// 該文檔並非真實數據,僅供測試使用\n// 命令:node InfectStatistic ${cmd}`
fs.writeFileSync(CmdParam.out[0], article, 'utf-8') //最后寫入文件
6、單元測試截圖和描述
1.正確性測試
2.日志記錄混合
測試結果
3.非法時間測試
最晚日志日期的當天,可以正常輸出
最晚日志日期的下一天,拋出錯誤
4.同時限定省份和類型
5.不提供日期參數,則設置為日志最大日期
6.mocha與chai單元測試框架結果
7、覆蓋率優化和性能測試,性能優化截圖和描述
尷尬的地方來了,
最強的覆蓋率工具Istanbul無法傳入命令行額外參數,這就使其永遠無法覆蓋到占比不小的參數處理部分,
覆蓋率也就失去了意義...
折騰了很久也沒能找到合適的替代品(也許,幾乎沒有人會在命令行node里傳額外參數吧)
所以我只能,把命令行參數處理部分換掉,如下:
分析:拋出Error錯誤的行數無法覆蓋,小部分二選一的分支無法覆蓋。


8、git倉庫鏈接、代碼規范鏈接
9、心路歷程和收獲
最大的感受就是,由於第一次接觸單元測試和覆蓋率,來回折騰了很久,尤其是對IDE沒有很好支持的人來說。
(jetbrains全家桶真的太強了...)
由於自己英語很爛,看很多插件和庫的教程也是迷迷糊糊,希望自己能更好提高探索未知技術的能力。
然后是撰寫博客和完備的測試,這對初入茅廬的我略有陌生,但是它對養成良好的代碼習慣和反思自己大有裨益。
最后是習慣於使用github非常重要,完善的項目管理功能可以幫助我們專注於代碼本身,而不必花心思在版本迭代、雲端備份等雞零狗碎的東西上。
10、技術路線圖相關的5個倉庫
新冠肺炎地圖:涉及很多地圖相關應用,以及很多eCharts特性的實踐
vue移動端耦合度更高的demo:基於移動端的vue項目,不過少部分特性已經過時
聊天室:簡易的websocket的demo,熟悉websocket-client的相關API
新冠肺炎數據:對丁香園網站的新冠肺炎數據爬取,並用eCharts進行匯總展示
es6全面的教程:但還是要結合實際,比如generator用的人實在太少了