2020软工第二次结对作业
2020软件工程第四次作业(结对编程)
-
这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2020 这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277 这个作业的目标 学习前端网页制作的基础知识 结对同学博客链接 https://www.cnblogs.com/blogwuhe/p/13795619.html 本作业博客链接 https://www.cnblogs.com/czy22655/p/13799947.html GitHub地址 https://github.com/rebuilder945/031802127-031802106 学号 031802106 031802127
一、具体分工
本次作业队友负责编码等许多重要任务,我对本次作业贡献较小,只进行了一些零散的工作。博客里的网站页面截图还未更新,属于旧版本,美化版本以发布在github。
二、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 120 |
Estimate | 估计这个任务需要多少时间 | 60 | 40 |
Development | 开发 | 300 | 400 |
Analysis | 需求分析 (包括学习新技术) | 60 | 120 |
Design Spec | 生成设计文档 | 60 | 50 |
Design Review | 设计复审 | 50 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
Design | 具体设计 | 120 | 120 |
Coding | 具体编码 | 100 | 100 |
Code Review | 代码复审 | 100 | 100 |
Test | 测试(自我测试,修改代码,提交修改) | 100 | 100 |
Reporting | 报告 | 200 | 250 |
Test Report | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 30 | 30 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 50 | 60 |
合计 | 1320 | 1620 |
三、解题思路描述与设计实现说明
解题思路:
主要分为两大部分:
-
格式化用户输入为Json对象
-
将Json对象可视化
首先将完整的程序流程展示如下:
其次说明思考过程:
- 网页知识学习问题:
我们都是没有前端经验的人,一开始将太多时间纠结于网页的实现过程了,我们花了大量时间来学习前端的知识。但发现这样实际是事倍功半的,且不说学习js、css、html的难度,也不谈学习的时间成本,关键在于先学后做的方式,使得解题实现过程中的很多问题没法在前面暴露出来,而前面学习过程中又无法有针对性地预测并学习这些未接触过、无法获知的问题,导致时间浪费,于是这样的方式显得愚蠢。所以我们在了解了基本网页知识后,就有针对性的寻找解题的途径。
- 树图的实现方式的寻找:
一开始是一头雾水,要自己一点点地画出来吗?那样的工作量以及学习成本,我们是没有办法完成的。bootstap、jquery等等又不知从何下手。问题关键在于将数据可视化处理成一棵树结构,那么有没有什么类似的库可以做到呢?
我们查询资料的方向变成了“如何用js实现树形图”,于是在网络上找到了一个类似的项目,可以将根据json文件内容生成树形图,仔细研究该项目的代码,发现生成树图的核心代码来自一个文件:d3.js,由于该文件代码量达到近1w行,于是猜测这就是我们所寻找的js数据可视化库,查找资料后验证了我们的猜想,于是一切变得简单起来了。
然后是实现过程:
我们引入d3.js库,将该项目的一部分实现树形图的代码封装成函数draw()以及display(),改变读入json的方式,并做适当修改,重用到我们的程序之中。其对外接口即json对象,而这时的json对象要靠我们解析用户输入文本得到。
因此接下来的工作就是解析用户输入。
解析用户输入:
将所给字符串格式化为json格式,需要依赖js中的对象,将文本内容所表示的含义结构化成对象,即可转化为json对象。
实现的函数为strParse(),代码如下:
function strParse() {
var tree = {
"name": '师门树',
"children": [
]
};
var teacher = {
"name": '',
"children": [
]
};
var degree = {
"name": '',
"children": [
]
}
var person = {
"name": '',
"children": [
]
}
var job = {
"name": '',
"children": [
]
}
var year = {
"name": '',
"children": [
]
}
//tree.children.push(teacher);
//tree.children.push(teacher);
//alert(JSON.stringify(tree));
var name = new String;
name = document.getElementById("textbox").value;
var num = 0;
var StuParts = [];
var teachers = name.split(/\n\n\n/g); //按导师分块
for (var i in teachers) {
StuParts[num] = teachers[i].split(/[\n, \n\n]/g); //导师
//内按行分块
delItem("", StuParts[num]); //取消空行
num++;
}
for (var i in StuParts) {
var k = -1;
var degrees = "";
var years = "";
var jobs = "";
var name = "";
var persons = [];
//parse persons first to be selected
for (var j = 1; j < StuParts[i].length; j++) {
k = -1;
jobs = "";
name = "";
if (!(StuParts[i][j][0] >= '0' && StuParts[i][j][0] <= '9')) {
while (StuParts[i][j][++k] != ":") //person name parse
{
name += StuParts[i][j][k];
}
person.name = name;
while (++k < StuParts[i][j].length) //job parse
{
if (StuParts[i][j][k] != '、') {
jobs += StuParts[i][j][k];
} else {
jobs = {
"name": jobs
};
person.children.push(jobs); //job insert
jobs = "";
}
}
jobs = {
"name": jobs
};
person.children.push(jobs);
jobs = "";
persons.push(person); //person union
var person = {
"name": '',
"children": [
]
}
}
}
//the above OK
//then parse degrees
for (var j = 1; j < StuParts[i].length; j++) {
k = -1;
degrees = "";
years = "";
name = "";
if (StuParts[i][j][0] >= '0' && StuParts[i][j][0] <= '9') {
while (StuParts[i][j][++k] != "级") { //years
years += StuParts[i][j][k];
}
while (StuParts[i][j][++k] != ":") { //degrees
degrees += StuParts[i][j][k];
}
year.name = years;
degree.name = degrees;
//person parse and insert year
while (++k < StuParts[i][j].length) {
if (StuParts[i][j][k] != '、') {
name += StuParts[i][j][k];
} else {
var flag = true;
for (var o in persons) {
if (persons[o].name == name) {
year.children.push(persons[o]);
flag = false;
break;
}
}
if (flag == true) {
person = {
"name": '',
"children": [
]
}
person.name = name;
year.children.push(person);
}
name = "";
}
} {
var flag = true;
for (var o in persons) {
if (persons[o].name == name) {
year.children.push(persons[o]);
flag = false;
break;
}
}
if (flag == true) {
person = {
"name": '',
"children": [
]
}
person.name = name;
year.children.push(person);
}
name = "";
}
//year insert degree and degree insert teacher
var flag = true;
for (var o in teacher.children) { //judge if degree `d in teacher
if (teacher.children[o].name == degree.name) {
teacher.children[o].children.push(year);
flag = false;
break;
}
}
if (flag == true) {
degree.children.push(year);
teacher.children.push(degree)
}
year = {
"name": '',
"children": [
]
}
degree = {
"name": '',
"children": [
]
}
}
}
var x = StuParts[i][0].indexOf(":");
var teacherName = "";
while (++x < StuParts[i][0].length) {
teacherName += StuParts[i][0][x];
}
teacher.name = teacherName;
tree.children.push(teacher);
teacher = {
"name": '',
"children": [
]
};
}
ijson = JSON.stringify(tree);
sessionStorage.setItem("x", ijson);
location.reload();
}
最后须将解析后的对象tree存入页面缓存sessionStorage,以使刷新网页时能够保留tree数据,再次加载html能够更新所需树形图。(详见 八、)这样整个解题过程就基本完成了,剩下的就是对页面的使用体验优化,以及bug修改(详见 八、)。可以点这里查看项目在这个时候的版本:
页面如下(经缩放的截图):
页面优化:
- 我们专注于页面用户体验,对页面输入做了美化,也修改了一些bug,最终的版本点这里
四、附加特点设计与展示
-
支持动态添加任意节点信息
在已显示父节点情况下,在目录结构中添加其的子节点,可以是任何信息描述,用以扩充信息; -
意义:方便用户补充遗漏信息,即使输入失误,也可以很快地重新修改;
演示如下: -
在目录结构中点击添加节点:
-
点击树图节点可以缩放显示/隐藏节点:
支持上传txt格式文件作为数据输入: -
将文件拖入框内即可,点击生成;
意义:提供多种方式以适应不同用户输入习惯以及使用需求,对于长文本可采用文件输入,对于短文本采用文本框输入。
演示如下:
- 点击输入数据按钮,跳出输入数据框(可拖动):
- 拖动文件上传数据:
五、目录说明和使用说明
目录说明:
- d3.v3.js
- d3库
- handle.js
- 封装了主要函数,包括strParse(), draw(), display(), delItem(),
- jQuery.2.2.1.js
- readme.md
- 说明
- main.html
- 页面
使用说明:
生成树图:
- 将文本复制粘贴进框内
- 点击按钮
- 生成树图
查看树图:
- 点击树图节点以缩放查看父节点或节点信息
添加节点:
- 在目录结构框中,鼠标移至要添加节点的父节点位置,出现加号按钮,点击
- 在弹出的消息框中输入加入节点的名称,点击确定
- 在图中查看新加入的节点
六、单元测试
采用基本的判定覆盖进行单元测试,由于draw()和display()是实现网页显示的函数,无返回结果,且程序核心函数为strParse(),只要该函数返回json对象结果正确,网页显示即可正确。故仅对strParse()函数进行测试如下:
$.getJSON("./testCase.json", function (data) { //从testCase.json中获取10个测试用例并测试
var testCase1 = data.testCase[0];
var testCase1_Expected = JSON.stringify(data.testCase_Expected[0]);
var testCase2 = data.testCase[1];
var testCase2_Expected = JSON.stringify(data.testCase_Expected[1]);
var testCase3 = data.testCase[2];
var testCase3_Expected = JSON.stringify(data.testCase_Expected[2]);
var testCase4 = data.testCase[3];
var testCase4_Expected = JSON.stringify(data.testCase_Expected[3]);
var testCase5 = data.testCase[4];
var testCase5_Expected = JSON.stringify(data.testCase_Expected[4]);
var testCase6 = data.testCase[5];
var testCase6_Expected = JSON.stringify(data.testCase_Expected[5]);
var testCase7 = data.testCase[6];
var testCase7_Expected = JSON.stringify(data.testCase_Expected[6]);
var testCase8 = data.testCase[7];
var testCase8_Expected = JSON.stringify(data.testCase_Expected[7]);
var testCase9 = data.testCase[8];
var testCase9_Expected = JSON.stringify(data.testCase_Expected[8]);
var testCase10 = data.testCase[9];
var testCase10_Expected = JSON.stringify(data.testCase_Expected[9]);
QUnit.test("strParse() test", function (assert) {
assert.equal(strParse_Test(testCase1), testCase1_Expected);
assert.equal(strParse_Test(testCase2), testCase2_Expected);
assert.equal(strParse_Test(testCase3), testCase3_Expected);
assert.equal(strParse_Test(testCase4), testCase4_Expected);
assert.equal(strParse_Test(testCase5), testCase5_Expected);
assert.equal(strParse_Test(testCase6), testCase6_Expected);
assert.equal(strParse_Test(testCase7), testCase7_Expected);
assert.equal(strParse_Test(testCase8), testCase8_Expected);
assert.equal(strParse_Test(testCase9), testCase9_Expected);
assert.equal(strParse_Test(testCase10), testCase10_Expected);
})
})
</script>
测试结果:
七、Github的代码签入记录截图
八、遇到的代码模块异常或结对困难及解决方法
文本解析问题
将文本转换成纯字符串硬解析,还是有什么取巧的办法(库)呢?最后决定使用strParse()函数进行硬解析。文本框直接读入的字符串比较特殊,一开始缺乏对多余的'\n' 以及 “” 的考虑,浪费很多时间;
- 解决方法:对特殊字符进行单独处理
页面更新问题与json传输方式问题:
由于用户需要在输入完成后,点击生成按钮来解析并生成,故需要页面的更新。如何将解析后的数据在刷新过程中保存下来呢?
- 解决办法:如果存到本地文件,刷新时读取,相关操作的浏览器不兼容问题导致无法真正存入本地文件,采用将解析后的数据存入页面缓存(只在退出页面时释放),并刷新的方式,来获取页面信息的更新。
九、评价你的队友
队友自学能力极强,负有责任感,对如何完成任务有着极为清晰有条理的规划,真- 人上人!