软工寒假作业(2/2)-疫情统计系统
这个作业属于哪个课程 | 2020春-s班 |
---|---|
这个作业要求在哪里 | 作业要求链接 |
这个作业的目标 | 学会使用github,设计一个疫情统计系统,学会一些基本的命令行操作 |
作业正文 | 作业正文 |
其他参考文献 | csdn、百度 |
1.Github仓库地址
2.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 45 | 50 |
Estimate | 估计这个任务需要多少时间 | 10 | 15 |
Development | 开发 | 920 | 1000 |
Analysis | 需求分析 (包括学习新技术) | 120 | 140 |
Design Spec | 生成设计文档 | 60 | 55 |
Design Review | 设计复审 | 20 | 25 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 25 |
Design | 具体设计 | 30 | 25 |
Coding | 具体编码 | 600 | 660 |
Code Review | 代码复审 | 60 | 70 |
Test | 测试(自我测试,修改代码,提交修改) | 340 | 270 |
Reporting | 报告 | 150 | 100 |
Test Repor | 测试报告 | 20 | 20 |
Size Measurement | 计算工作量 | 20 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1535 | 1500 |
3、我的设计思路描述
3.1 首先分析日志文本
福建 新增 感染患者 23人
福建 新增 疑似患者 2人
浙江 感染患者 流入 福建 12人
湖北 疑似患者 流入 福建 2人
安徽 死亡 2人
* 治愈 3人
福建 疑似患者 确诊感染 2人
* 排除 疑似患者 5人
// 该文档并非真实数据,仅供测试使用
可以得知一共有六大种情况
1.新增
2.省A 流入 省B
3.死亡
4.治愈
5.排除
6.确诊
然后分析得知:1.死亡和治愈的一定是感染患者 2.排除和确诊的一定是疑似患者
3.2 代码需要实现的几个关键功能
1.读取文件
2.写入文件
3.解析命令行
4.处理日志文件的数据
5.统计全国的数据
3.3 确定数据结构
设计一个省类来存储每个省的名称、感染患者、疑似患者、治愈、死亡人数等信息
还有一些获取相关信息的函数
class InfectStatistic {
class Province{
String name=null;//省份名称
int ip=0;//感染患者
int sp=0;//疑似患者
int cure=0;//治愈
int dead=0;//死亡
public Province(String name,String num,String type) {
this.name=name;
if(type=="ip") {
this.ip=Integer.valueOf(num);
}
else if(type=="sp") {
this.sp=Integer.valueOf(num);
}
}
······
}
4、设计实现过程
4.1 思路流程展示(只展示了关键函数)
4.2 处理命令行参数
java InfectStatistic list -date 2020-01-22 -log D:/log/ -out D:/output.txt
list命令 要求:
-log 指定日志目录的位置,该项必会附带
-out 指定输出文件路径和文件名,该项必会附带
-date 指定日期,不设置则默认为所提供日志最新的一天。你需要确保你处理了指定日期以及之前的所有log文件
-type 可选择[ip,sp,cure,dead] 若指定则按指定顺序输出,不指定该项默认列出所有情况。
-province 指定列出的省,不指定则列出全国以及日志中出现的省份
4.3 处理各省数据
4.4 统计全国数据
遍历省份哈希表(我命名为proHash),将所有数据相加,再将统计后的数据存入哈希表
4.5 写入文件
5、代码说明
5.1.1 解析命令行
//解析命令行
public void dealCmd(String[] test,Hashtable<String,Province> proHash) throws IOException {
Hashtable<String,Integer> cmdHash=new Hashtable<String,Integer>();
Hashtable<Integer,String> typeHash=new Hashtable<Integer,String>();
String[] province=new String[32];
for(int i=0;i<test.length;i++) {
cmdHash.put(test[i], i);
}
//将文件名的日期分割后存放于此
String[] splitDate=new String[3];
boolean flagDate=false;
//处理-date
if(cmdHash.containsKey("-date")) {//如果给出-date
splitDate=test[cmdHash.get("-date")].split("-");
dealDate(test[cmdHash.get("-log")+1],0,test[cmdHash.get("-date")+1],proHash,flagDate);
}
else {
//如果没有给出具体日期,获取当前系统日期传给date
flagDate=true;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String currentDate = dateFormat.format(new Date());
dealDate(test[cmdHash.get("-log")+1],0,currentDate,proHash,flagDate);
}
Sum(proHash);
//处理-province
if(cmdHash.containsKey("-province")) {
int count=0;
int i;
int num=cmdHash.get("-province")+1;
for(i=num;i<test.length;i++) {
if(test[i].equals("-")) {
count=i-num+1;
break;
}
}
if(count==0&&i==test.length) {//如果-province后面没有别的指令
count=i-num;
}
for(int j=0;j<province.length;j++) {
if(num+j<test.length)
province[j]=test[num+j];
else
province[j]="无";
}
}
else
province=null;
//处理-type
if(cmdHash.containsKey("-type")) {
int num=cmdHash.get("-type")+1;
for(int i=num;i<test.length;i++) {
if(!test[i].substring(0,1).equals("-")) {
typeHash.put(i-num,test[i]);
}
}
}
else
typeHash=null;
writeFile("F:/output.txt",proHash,typeHash,province);
}
由于-date -province -type 输入的内容不同产生的结果也不同,因此应该进行相应的处理,我用Hashtable存储type参数,用String数组存储province参数,处理完以后将结果作为参数传给writeFile()函数
5.1.2 处理日期对象
//处理日期对象,比较日期大小,判断哪些文件需要处理
private void dealDate(String filepath,int deep,String date,Hashtable<String,Province> hashtable,boolean flag) throws IOException{
// 获得指定文件对象
File file = new File(filepath);
// 获得该文件夹内的所有文件
File[] childFile = file.listFiles();
for(int i=0;i<childFile.length;i++)
{
if(childFile[i].isFile())//如果是文件
{
if(date.compareTo(childFile[childFile.length-1].getName().substring(0,10))>0&&flag==false) {
System.out.println("超出日期范围!!!");
break;
}
//比较日期大小
String fileName=childFile[i].getName().substring(0,10);
if(date.compareTo(fileName)>=0) {
dealStatistic(childFile[i].getPath(),hashtable);
}
}
}
}
通过对读取的文件夹进行处理,获取里面所有的.log文件,与命令行输入的日期进行比较,判断应该处理哪些文件以及给出的日期是否合法,并作出相应处理。
5.2 处理各省份的数据
按行读取.log文件,并将每行的内容以空格划分成几个字符串,将字符串存入splitString数组
情况一:
if(!splitString[0].equals("//")){
if(splitString.length==4){//新增 确诊 排除 三种情况
if(splitString[1].equals("新增")) {
if(splitString[2].equals("感染患者")) {
······
}
else if(splitString[2].equals("疑似患者")){//如果是疑似患者
······
}
}//if新增结束
else if(splitString[2].equals("确诊感染")) {//只有疑似患者会确诊
hashtable.get(splitString[0]).addNum(getNumber(splitString[3]),"ip");
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[3]),"sp");
}
else if(splitString[1].equals("排除")){//排除 只有疑似患者能排除
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[3]), "sp");
}
}
情况二:
else if(splitString.length==5) {//这种情况只有流入一种情况
if(hashtable.containsKey(splitString[3])==false) {//如果province中没有这个省份则新建
String proName;
if(splitString[1].equals("感染患者")) {
//被流入的省份ip直接初始为化流入值
hashtable.put(splitString[3],new Province(splitString[3],getNumber(splitString[4]),"ip"));
//流出ip的省份减
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[4]), "ip");
}
else if(splitString[1].equals("疑似患者")){
//proName=getProvinceName(splitString[3]);
hashtable.put(splitString[3], new Province(splitString[3],getNumber(splitString[4]),"sp"));
//流出sp省份减
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[4]), "sp");
}
}
else{//如果已有这个省份
if(splitString[1].equals("感染患者")) {
hashtable.get(splitString[3]).addNum(getNumber(splitString[4]),"ip");
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[4]), "ip");
}
else {
hashtable.get(splitString[3]).addNum(getNumber(splitString[4]),"sp");
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[4]), "sp");
}
}
}
情况三:
else{//splitString.length==3治愈或死亡 两种情况,并且死亡和治愈的一定是感染患者
if(splitString[1].equals("死亡")) {
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[2]),"ip");
hashtable.get(splitString[0]).addNum(getNumber(splitString[2]),"dead");
}
else if(splitString[1].equals("治愈")) {
hashtable.get(splitString[0]).deleteNum(getNumber(splitString[2]),"ip");
hashtable.get(splitString[0]).addNum(getNumber(splitString[2]),"cure");
}
}
5.3 统计全国数据
//统计全国数据
public void Sum(Hashtable<String,Province> hashtable) {
Province nation=new Province("全国");
Set set=hashtable.keySet();
Iterator iterator=set.iterator();
while(iterator.hasNext()) {
Object keys=iterator.next();
nation.ip+=hashtable.get(keys).getIp();
nation.sp+=hashtable.get(keys).getSp();
nation.cure+=hashtable.get(keys).getCure();
nation.dead+=hashtable.get(keys).getDead();
}
hashtable.put("全国",nation);
}
通过对哈希表的遍历去统计全国的数据,最后将全国的数据存入哈希表
5.4 写文件
不指定省份的情况下
if(province==null) {//不指定省份
File file = new File(path);
file.createNewFile();
fileWriter = new FileWriter(file.getAbsoluteFile(),true);
bw = new BufferedWriter(fileWriter);
try {
for(int i=0;i<32;i++) {
if(proHash.containsKey(sortedPro[i])) {
String keys=sortedPro[i];
if(typeHash!=null) {
content=keys;
for(int k=0;k<typeHash.size();k++) {
if(typeHash.containsKey(k)) {
String s=typeHash.get(k);
switch(s) {
case "ip":
content+=" 感染患者"+proHash.get(keys).getIp()+"人";
break;
case "sp":
content+=" 疑似患者"+proHash.get(keys).getSp()+"人";
break;
case "cure":
content+=" 治愈"+proHash.get(keys).getCure()+"人";
break;
case "dead":
content+=" 死亡"+proHash.get(keys).getDead()+"人";
break;
}
}
}
content+="
";
}
else
content=proHash.get(keys).getName()+" 感染患者"+proHash.get(keys).getIp()+"人 疑似患者"+proHash.get(keys).getSp()+"人 治愈"+proHash.get(keys).getCure()+"人 死亡"+proHash.get(keys).getDead()+"人
";
bw.write(content);
}
}
bw.write("// 该文档并非真实数据,仅供测试使用
");
bw.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
指定省份的情况下
else if(province!=null){//有指定省份
File file = new File(path);
for(int i=0;i<32;i++) {
for(int j=0;j<province.length;j++) {
if(proHash.containsKey(province[j])&&province[j]==sortedPro[i]&&!province[j].equals("无")) {//哈希表中有此省份
String keys=sortedPro[i];
if(typeHash!=null) {
content=keys;
for(int k=0;k<typeHash.size();k++) {
if(typeHash.containsKey(k)) {
String s=typeHash.get(k);
switch(s) {
case "ip":
content+=" 感染患者"+proHash.get(keys).getIp()+"人";
break;
case "sp":
content+=" 疑似患者"+proHash.get(keys).getSp()+"人";
break;
case "cure":
content+=" 治愈"+proHash.get(keys).getCure()+"人";
break;
case "dead":
content+=" 死亡"+proHash.get(keys).getDead()+"人";
break;
}
}
}
content+="
";
}
else
content=proHash.get(keys).getName()+" 感染患者"+proHash.get(keys).getIp()+"人 疑似患者"+proHash.get(keys).getSp()+"人 治愈"+proHash.get(keys).getCure()+"人 死亡"+proHash.get(keys).getDead()+"人
";
fileWriter = new FileWriter(file.getAbsoluteFile(),true);
bw = new BufferedWriter(fileWriter);
bw.write(content);
bw.close();
}
else if(!proHash.containsKey(province[j])&&province[j]==sortedPro[i]){//哈希表中没有这个省份
String keys=sortedPro[i];
content=keys;
if(typeHash!=null) {
for(int k=0;k<typeHash.size();k++) {
if(typeHash.containsKey(k)) {
String s=typeHash.get(k);
switch(s) {
case "ip":
content+=" 感染患者0人";
break;
case "sp":
content+=" 疑似患者0人";
break;
case "cure":
content+=" 治愈0人";
break;
case "dead":
content+=" 死亡0人";
break;
}
}
}
content+="
";
}
else
content=sortedPro[i]+" 感染患者0人 疑似患者0人 治愈0人 死亡0人
";
fileWriter = new FileWriter(file.getAbsoluteFile(),true);
bw = new BufferedWriter(fileWriter);
content+="// 该文档并非真实数据,仅供测试使用
";
bw.write(content);
bw.close();
}
}
}
}
6、单元测试
6.1 单元测试1
指定date为2020-01-30 给出超出日期提示
6.2 单元测试2
指定date为2020-01-22 不指定省份和类型
6.3 单元测试3
指定date为2020-01-27 不指定省份和类型
6.4 单元测试4
指定type为ip 不指定省份
6.5 单元测试5
仅给出-log 和-out
6.6 单元测试6
指定-province为福建、湖北
6.7 单元测试7
指定省份既有日志中存在的,又有日志中不存在的
6.8 单元测试8
在测试7的基础上指定type
6.9 单元测试9
指定type并打乱顺序,测试是否会按顺序输出
6.10 单元测试10
多指定几个省份,测试省份排序是否正确
7、优化
7.1 优化前
可以发现单元覆盖率较低的部分是函数dealStatistic(),于是修改了一些部分
7.2 优化后
优化后dealStatistic()单元覆盖率达到90.5%,整体覆盖率达到95%
8、代码规范
9、心路历程与收获
一开始看到这次作业的要求时,我是有点懵的,因为作业要求里出现的很多名词都是以前没有接触过或只是略有耳闻的,如PSP表格、JUNIT等等。所以在开始开发前我查阅了一些资料,首先了解了这些东西是用来干嘛的。在github的fork过程中也碰到了一些问题,参考了老师给的一些资料以及求助同学最终解决了问题。
在开发过程中,对需求分析以及一些设计流程进行了巩固,还复习了java的基础语法,以及如何使用JUnit。
通过这次实践作业我学习了github的相关知识,学会了一些基本的Git命令以及Github Desktop的一些功能,学会如何fork项目以及commit,将本地的代码push到网站上;还学习了单元测试、性能优化以及PSP表格相关的知识,对如何与他人合作完成项目有了初步的认识。
10、前端相关的5个仓库
1.bootstrap
描述:bootstrap是一种流行的前端框架,这个仓库里面有非常多相关知识
2.JQuery
描述:jQuery是一个简洁而快速的JavaScript库,可用于简化事件处理,这个仓库用于学习JQuery相关知识
3.AJAX
描述:受jQuery/zepto启发的独立AJAX库
4.JavaScript
描述:JavaScript样式指南,包含JavaScript语法知识
5.PHP
描述:PHP语言以及WEB网站开发整理的一些资源,有视频,实战代码,学习路径等,会持续更新。