2020软件工程第二次结对作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/SE2020 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277 |
这个作业的目标 | 学习HTML、CSS、JavaScript、框架使用等相关知识以及github的协作 |
学号 | 041802216(刘新伟)、 061800508(高体民) |
结对同学博客链接 | https://www.cnblogs.com/ggg-ooo/ |
GitHub项目地址 | https://github.com/ggg0919/061800508-041802216 |
具体分工
041802216刘新伟:主要负责编写js代码、单元测试、归纳总结
061800508高体民:主要负责html、css,素材收集和页面设计,博客园编写
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
Estimate | 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | ||
Analysis | 需求分析 (包括学习新技术) | 800 | 850 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
Design | 具体设计 | 100 | 110 |
Coding | 具体编码 | 800 | 900 |
Code Review | 代码复审 | 60 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 140 |
Reporting | 报告 | 60 | 60 |
Test Report | 测试报告 | 30 | 30 |
Size Measurement | 计算工作量 | 15 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 30 |
合计 | 2125 | 2315 |
解题思路描述与设计实现说明
解题思路
我们需要设计一个网页,可以在里面在文本框输入信息,根据固定的格式提取关键信息,然后在此网页上生成对应的家族树。
难点分解:
(1)如何设计一个网页
(2)如何得到文本框的输入数据
(3)如何处理文本数据
(4)如何生成学术家族树
针对以上四大难点,通过阅读前人的博客和搜索网上的资料,决定采取bootstrap+jquery+d3的总体框架来实现本次任务。
数据流图:
数据处理算法的流程图:
关键代码:
框架调用与页面设计
界面采用多个区域分块搭配颜色,并且引用了图片作为背景,较为酷炫
实现思路为外部css文件调整各项文本框区域数值,html引用页面布局
代码如下:
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<title>学术家族树</title>
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="js/jquery.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<link type="text/css" rel="stylesheet" href="type.css">
</head>
<body>
<script src="js/d3.v3.min.js"></script>
<div class="flex-container">
<p id="title">家族树</p>
<textarea type="textarea" style="margin-top: 50px" id="text" cols="60" rows="10" class="center"
placeholder="请输入内容"></textarea>
<button href="javascript:;" onclick="getdata()" class="button_left">建立家族树</button>
<input type=button value=刷新 onclick="location.reload()" class="button_right">
</div>
<div class="svg-container">
数据的解析
总体思想就按上面的流程图把数据层层展开:先以导师为分组,在以空行为分组,再以职称、年级、姓名、技能层层递进,注意输入格式,关键的空行,:,、不能少。
function getdata() {
$('.svg-container').empty();
var text = $("#text").val(); //获取id为text的textarea的全部内容
var text2 = $("#text").val().split(/导师[:|:]?/g);
var new_add = $("#text").val().split(/\n[^(导师)][:|:]?/g); // 追加内容
var new_add_flag = []; // 追加内容的标识;
//console.log(new_add);
var text3 = text2.filter(item => { // 过滤数组空元素
if (item.length > 1) {
return item;
}
});
for (let i = 0; i < text3.length; i++) {
text3[i] = '导师:' + text3[i];
}
var text1 = text.split("\n\n");//针对多组数据输入的情况,以“\n\n"为关键字进行分组,调用split函数进行分割
//console.log(text1);
//console.log(text3);
text1 = text3;
for (var k = 0; k < text1.length; k++) { //text1.length用于得到分组的数量
//console.log(text1[k]);
new_add_flag = [];
new_add = text1[k].split(/\n[^(导师)][:|:]?/g);
new_add = new_add.filter(function (item) {
let rexp = new RegExp(/[0-9]+级?/, 'ig');
//console.log(rexp, item.indexOf('导师') == -1, !/[0-9]+级?/ig.test(item));
if (item.indexOf('导师') == -1 && !/[0-9]+级?/ig.test(item)) {
new_add_flag.push(item.replace(/[:|:]+[^0]+/, ''));
return item;
}
});
//console.log('过滤', new_add, new_add_flag);
var arry = text1[k].split("\n");//针对每一组数据,以“\n"为关键字进行分组,得到每条导师和学生的信息
var teacher = {
name: '',
nianji: '',
children: []
};
let zhiChengObj = {}; // 职称分类对象集合
let zhicheng_arr = []; // 全部职称是数组
let nianji_arr = []; // 年级的集合
let nianji = [];
for (var ii = 0; ii < arry.length; ii++) { // for2判断有没有重复的职称
if (/\n+/.test(arry[ii]) || arry[ii].length <= 1) {
//console.log('匹配成功');
} else {
var newarr = arry[ii].split(":");//针对每条导师和学生的信息,以“:”为关键字进行分组,可
//得到身份标签和身份信息
let go_on = true;
new_add_flag.forEach(function (item) {
//console.log(item, newarr, newarr.includes(item.trim()));
if (newarr.includes(item.trim())) {
go_on = false;
}
});
if (go_on) {
//console.log(arry[ii]);
//console.log(newarr);
var a1 = newarr[0]; //获取身份标签,如导师、2016级博士生等,保存在a1变量
let strIndex = a1.indexOf('级');
let nianjiItem = a1.substring(0, strIndex + 1); // 年级
let zhicheng = a1.substring(strIndex + 1); // 职称
//console.log(nianjiItem);
var type = [];
var type1 = [];
if (ii != 0) {
type1.name = zhicheng; // 职称
}
var a2 = newarr[1]; //获取身份信息,如天一、王二、吴五等,保存在a2变量 [字符串]
//console.log(a2);
var a3 = a2.split("、"); //针对每组身份信息,以“、”为关键字进行切分,得到每个人的名字信息 [数组]
if (!zhicheng_arr.includes(zhicheng)) { //&& zhicheng.indexOf('生')!= -1
//console.log('已经存在', zhicheng);
zhicheng_arr.push(zhicheng);
if (!zhiChengObj[zhicheng]) {
zhiChengObj[zhicheng] = [];
}
zhiChengObj[zhicheng].push(...a3);
nianji_arr.push(nianjiItem);
} else { //if (zhicheng.indexOf('生')!= -1)
if (!zhiChengObj[zhicheng]) {
zhiChengObj[zhicheng] = [];
}
zhiChengObj[zhicheng].push(...a3);
nianji_arr.push(nianjiItem);
}
}
}
}
nianji_arr = nianji_arr.filter(function (item) {
if (item.length > 1) return item;
});
let currentIndex = 0;
for (let key in zhiChengObj) {
let type = [];
let type1 = [];
let zhicheng = key; // 职称
let nianji = [];
let is_daoshi = key.indexOf('导师') != -1 ? true : false; // 是不是导师
let zhicheng_val = zhiChengObj[key]; // 职称对应的值
zhicheng_val.forEach(function (item, j) {
let student = {};
if (is_daoshi) {
teacher.name = item;
} else {
student.name = item; //zhicheng_val[j]
new_add_flag.forEach(function (flag, ind) {
//console.log(flag, item);
if (flag == item) {
let cont = new_add[ind].replace(/[^0]*[:|:]+/, '');
let cont_arr = cont.split('、');
//console.log(cont_arr);
cont_arr.forEach(function (child) {
if (!student.children) {
student.children = [];
}
student.children.push({name: child});
});
}
});
type.push(student);
// //console.log(student);
}
});
if (is_daoshi == false) {
type1.name = zhicheng; // 职称
nianji.push({name: nianji_arr[currentIndex], children: type}); // 年级
}
if (is_daoshi == false) {
// type1.children = type;
type1.children = nianji;
teacher.children.push(type1);
}
//console.log(teacher);
currentIndex++;
}
treeData[k] = [];
treeData[k] = teacher;
maketree(k);
}
}
附加特点设计与展示
- 我们在这个网页中实现了多个导师,多棵树并存的情形
- 能够支持多组数据的连续输入,不断生成新树
- 我们在页面文本框的底部添加了一个刷新按钮,考虑到用户可能输错数据或者在实现一组家族树后想要继续输入数据的需求,可以比较快捷地进行更改
- 我们在页面上插入了一些背景图片来进行美化界面;
实现思路
数据处理时多个导师信息就存在不同的数组里,最后遍历循环创建树。
在网上学习d3.js库和一些前端知识来一步步实。具体d3教程可见d3教程。
有价值的代码展示
实现刷新功能:
<input type=button value=刷新 onclick="location.reload()" class="button_right">
利用css选择器来插入背景图片以及美化字体和文本框:
body { //整体框架结构位置分布
margin: 0px;
padding: 0px;
background-image: url('images/tig.jpg');//通过此命令插入背景图片
background-size: 100%;
background-repeat: repeat;
background-size: cover;
text-align: center;
}
p {
margin: 0;
padding: 0;
}
.flex-container {
background-size: 100% 100%;//背景的填充
background-attachment: fixed;
}
.center {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: auto;
margin-bottom: auto;
opacity: 0.4;
}
.button_left { //按钮的位置
position: absolute;
left: 410px;
width: 100px;
padding: 4px;
opacity: 0.4;
}
.button_right {
position: absolute;
right: 410px;
width: 100px;
padding: 5px;
opacity: 0.4;
}
.node {
cursor: auto;
}
.node circle {
fill: rgb(19, 2, 17);
stroke: rgb(14, 10, 6);
stroke-width: 3px;
}
.node rect {
fill: #3d8cd6;//节点框中的颜色
stroke:#e60d72;//节点框中边缘的颜色
stroke-width: 5px;
}
.node text {
font: 12px "楷体", sans-serif;
}
.link {
fill: none;
stroke: rgb(41, 226, 177);//连线的颜色
stroke-width: 2px;
}
#title {
text-align: center;
color: rgb(4, 4, 7);
font: 35px "楷体", sans-serif;//title的字体
font-weight: normal;
line-height: 50px;
text-shadow: rgb(229, 229, 233) 2px 2px 3px;//标题带上字体阴影
background: rgb(159, 105, 190);
}
效果展示
一组基本数据输入
结点缩放功能
多组数据输入
目录说明和使用说明
-
bootstrap:bootstrap框架文件
-
css:网页的css样式文件
-
images:插入的图片
-
js:html文件中需要导入的一些js文件
-
type:html中需导入的css文件
-
README:目录说明与使用说明文件
-
学术家族树:html文件
使用说明:
- 使用
git clone https://github.com/ggg0919/061800508-041802216.git
命令将仓库克隆至本地 - 使用谷歌浏览器打开学术家族树.html。
- 输入格式就按题目要求,多组数据之间空一行输入,不需要空两行,技能与经历也需空行输入。进行多次测试时,需要按刷新按钮。
单元测试
测试工具:Mocha
学习方法:
我们是通过一个学习网站:[阮一峰教程](http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html) 进行学习,里面讲解的很详细。
以下是我的个人教程:
1.首先得先拥有Git,大家肯定都有在键盘上按Shift+右键,可进入Powershell页面,之后在输入指令git clone https://github.com/ruanyf/mocha-demos.git
,将其克隆到自己的电脑上。
2.随后按照指令
$cd mocha-demos
$ npm install
对mocha进行安装,(电脑必须要安装Node,否则会失败)
在使用Mocha的时候,我碰到了一个情况,显示再此系统上禁止运行此脚本。之后我百度得知,要用管理员权限打开Powershell,并且按照如下操作:
即可解决问题,接下来就按照我给出的网站进行学习即可。注意在编写单元测试代码的时候要严格按照网站给出的代码执行,否则系统会报错。
对输入文本进行数据处理函数的测试:
测试人员的***难:
1.测试人员会采用对单棵树不同数据格式来测试我们的数据;
2..测试人员会采用对多棵树不同数据格式来测试我们的数据;
3.测试人员会对多棵树的内容进行测试:比如导师名相同的情况;
构造测试数据的输入思路
- 首先对一棵标准家族树的样例的进行测试;
- 对一棵家族树的输入格式不标准的样例进行测试;
- 对单棵家族树输入数据的扩大:每行输入的学生更多;输入很多行的情况;
- 对多棵家族树的正常情况进行测试;
- 对多棵家族树中存在输入格式不标准情况进行测试;
- 对多棵家族树中存在导师名相同的情况进行测试;
Github的代码签入记录截图
代码模块异常或结对困难及解决方法
(1)问题描述:不会 HTML + CSS + JavaScript,这次作业是纯前端,我和队友都没有接触过,只能从零开始了。
解决方案:学呗,还能咋样
是否解决:已经解决
收获:这次作业让我感受到了前端的魅力,我和队友都很努力的学习前端技能,也最终做出了想要的页面。
(2)问题描述:针对多组师生信息的输入,不知道以什么为关键字为标志信息,用什么办法拆分,一直无法正确进行切分,提取出每组的师生信息
做过哪些尝试:上网百度、询问同学
是否解决:已经解决
收获:提升了自己分析问题和解决问题的能力,并且对JavaScript的认识有了进一步的提高。
(3)问题描述:在实现了生成单棵师门树和多棵师门树共存的基础上,一直没办法呈现两颗关联树共存的形式(自闭....)
做过哪些尝试:不断地修改代码、去网上搜索相关信息
是否解决:未解决
收获:虽然没办法呈现两颗关联树共存的形式,但是在不断的修改代码和去网上学习相关知识的过程中,既锻炼了自己也学到了很多新知识
(4)问题描述:用mocha进行单元测试时,不会编写测试脚本。
解决方案:查看网上的一些教程视频,自己进行脚本编写,在经过大概三小时的学习中,终于写出了测试脚本。
是否解决:已解决
队友互评与总结:
刘新伟:我的队友是一个肯刻苦钻研的人。这次的结对编程作业涉及很多前端的知识,像HTML、css、JavaScript等前端设计的基础知识,都是他用这几天时间去学习。我也学习了一些技能,从零开始的web端学习实在是太痛苦了,在前端设计完之后,我也参与了后端生成树的设计,这让我对于Web设计的流程以及各个部分都有了大致的了解。在组队的过程中我们配合地非常默契默契,分工明确,即使有一些分歧的时候他也能够很耐心地和我去磨合,他是一个很不错的队友,希望以后有机会我们还能再见一次凌晨四点的福大!
高体民:我的队友是认真努力的人,这次多亏他的全面帮助,我们的数据处理才能有效的实现,结对项目才能如此顺利,通过这次结对,从队友身上学到了很多。在这次结对中,我对前端与后端有了一些了解,学习了html和css的应用后立马又转向了对bootstrap框架的学习。这次结对训练完成以后还要继续加强学习才好,不停地求索进步。个人觉得我们两个在结对配合和交流上相对比较顺畅,总能适时地交流意见和建议,我在网上看了那么多教程,不如自己亲手实践来的感触更深,这大概就是张栋老师所说的learning by doing吧。尽管一路摸爬滚打过来,但是在最后能看到一个成型的网站,我们还是非常开心的。队友很给力,希望以后有机会我们还能再见一次凌晨四点的福大!