一、前言
這個做業屬於哪一個課程 | 班級連接 |
這個做業要求在哪裏 | 做業要求 |
這個做業的目標 | 使用github、規範代碼 |
做業正文 | 做業正文 |
其餘參考文獻 | 暫無 |
二、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 |
三、思路描述
<img src="https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200205215926796-2062364536.png" style="width:200px"> ![]()html
四、實現過程
<img src="https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200205222243740-754093877.png" style="width:220px"> ![]()vue
五、代碼說明
這裏是代碼地址,有詳細註釋
命令行參數與統計結果的數據結構以下:
代碼僅由如下三部分構成,共130餘行:node
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') //最後寫入文件
六、單元測試截圖和描述
1.正確性測試
2.日誌記錄混合
<!--<img src="https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200205232016472-1469081961.png" style="width:500px">-->git
測試結果
<!--<img src="https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200206113916675-940302290.png" style="width:700px">-->es6
3.非法時間測試
最晚日誌日期的當天,能夠正常輸出
最晚日誌日期的下一天,拋出錯誤
4.同時限定省份和類型
5.不提供日期參數,則設置爲日誌最大日期
6.mocha與chai單元測試框架結果
七、覆蓋率優化和性能測試,性能優化截圖和描述
尷尬的地方來了, 最強的覆蓋率工具Istanbul沒法傳入命令行額外參數,這就使其永遠沒法覆蓋到佔比不小的參數處理部分, 覆蓋率也就失去了意義... 折騰了好久也沒能找到合適的替代品(也許,幾乎沒有人會在命令行node裏傳額外參數吧) 因此我只能,把命令行參數處理部分換掉,以下: 分析:拋出Error錯誤的行數沒法覆蓋,小部分二選一的分支沒法覆蓋。github
<div style="display:flex;"> <img src="https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200205233259638-1567344822.png"> <img src="https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200205235227733-1355919960.png"> </div> 減小代碼耦合會使代碼更簡潔易懂、後期維護成本低,但也會略微損失性能,比方說用迭代器遍歷對象的鍵。 性能優化方面,在採起「正則預編譯」、「手動列省份順序來替換漢字排序localeCompare()」的措施後,耗時明顯下降。 ![](https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200214181125789-844964291.png) ![](https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200214181132612-639554039.png) ![](https://img2018.cnblogs.com/blog/1918331/202002/1918331-20200214181152453-399772363.png) 待完善的部分:代碼風格的面向對象部分不足;改成異步讀寫文件會提升效率,但因爲處理順序的限制,理論上講性能提高頗有限。web
八、git倉庫連接、代碼規範連接
九、心路歷程和收穫
最大的感覺就是,因爲第一次接觸單元測試和覆蓋率,來回折騰了好久,尤爲是對IDE沒有很好支持的人來講。 (jetbrains全家桶真的太強了...) 因爲本身英語很爛,看不少插件和庫的教程也是迷迷糊糊,但願本身能更好提升探索未知技術的能力。 而後是撰寫博客和完備的測試,這對初入茅廬的我略有陌生,可是它對養成良好的代碼習慣和反思本身大有裨益。 最後是習慣於使用github很是重要,完善的項目管理功能能夠幫助咱們專一於代碼自己,而沒必要花心思在版本迭代、雲端備份等雞零狗碎的東西上。websocket
十、技術路線圖相關的5個倉庫
新冠肺炎地圖:涉及不少地圖相關應用,以及不少eCharts特性的實踐 vue移動端耦合度更高的demo:基於移動端的vue項目,不過少部分特性已通過時 聊天室:簡易的websocket的demo,熟悉websocket-client的相關API 新冠肺炎數據:對丁香園網站的新冠肺炎數據爬取,並用eCharts進行彙總展現 es6全面的教程:但仍是要結合實際,好比generator用的人實在太少了數據結構