2020软工第二次结对作业

2020软件工程第四次作业(结对编程)

一、具体分工

本次作业队友负责编码等许多重要任务,我对本次作业贡献较小,只进行了一些零散的工作。博客里的网站页面截图还未更新,属于旧版本,美化版本以发布在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
    • 页面

使用说明:

生成树图

  1. 将文本复制粘贴进框内
  2. 点击按钮
  3. 生成树图

查看树图

  1. 点击树图节点以缩放查看父节点或节点信息

添加节点:

  1. 在目录结构框中,鼠标移至要添加节点的父节点位置,出现加号按钮,点击
  2. 在弹出的消息框中输入加入节点的名称,点击确定
  3. 在图中查看新加入的节点

六、单元测试

采用基本的判定覆盖进行单元测试,由于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传输方式问题:
由于用户需要在输入完成后,点击生成按钮来解析并生成,故需要页面的更新。如何将解析后的数据在刷新过程中保存下来呢?

  • 解决办法:如果存到本地文件,刷新时读取,相关操作的浏览器不兼容问题导致无法真正存入本地文件,采用将解析后的数据存入页面缓存(只在退出页面时释放),并刷新的方式,来获取页面信息的更新。

九、评价你的队友

队友自学能力极强,负有责任感,对如何完成任务有着极为清晰有条理的规划,真- 人上人!

posted @ 2020-10-11 22:44  假诗人  阅读(114)  评论(0编辑  收藏  举报