结对编程之学术家族树

这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2020
作业博客链接 https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277
GitHub项目地址 https://github.com/AJiang0704/031802309-031802320

一、结对说明

学号 姓名 博客链接 具体分工
031802309 郭盈江 https://www.cnblogs.com/AJiang0704/p/13802771.html 算法设计、UI设计、代码编写
031802320 刘国强 https://www.cnblogs.com/supernoob/p/13802522.html 分析归纳、收集素材、单元测试

二、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
Estimate 估计这个任务需要多少时间 30 20
Development 开发
Analysis 需求分析 (包括学习新技术) 60 120
Design Spec 生成设计文档 20 30
Design Review 设计复审 40 40
Coding Standard 代码规范 (为目前的开发制定合适的规范) 40 30
Design 具体设计 40 40
Coding 具体编码 360 520
Code Review 代码复审 20 40
Test 测试(自我测试,修改代码,提交修改) 120 240
Reporting 报告
Test Report 测试报告 60 60
Size Measurement 计算工作量 30 30
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 90 120
合计 910 1290

三、解题思路描述与设计实现说明

代码实现思路:

js 接收到从文本框输入的数据后,根据关键字进行解析,逐次分组切割,并依次将它们嵌入节点,以构造树型结构。
然后绘制树状图,使用 d3.js 可以得到可视化树图,显示在 web 页面。

数据流图:

核心代码:

GetData 函数用于接收并处理数据。

/** 
 *接收并解析数据以构造 json 树结构
 */
function GetData() {
    document.getElementById('show-tree').innerHTML = '';
    var text = document.getElementById("input-text").value;
    if (!text) alert("输入不能为空哦");
    var mul_data = text.split("

");
    var h = 0;
    var hide = [];
    var count_tree = 0;
    for (var i = 0; i < mul_data.length; i++) {
        count_tree++;
        var pic_data = mul_data[i].split("
"); 
        var line = 0;
        for (var j = 0; j < pic_data.length; j++) {
            var per_data = pic_data[j].split(":"); 
            var per_front = per_data[0];
            var per_back = per_data[1];
            if (per_front == "导师") {
                var teacher = {
                    "name": per_back,
                    "parent": null,
                    "children": []
                }
                TreeData[i] = teacher;
            } else if(per_front.search("博士生") >= 0 || per_front.search("硕士生") >= 0 || per_front.search("本科生") >= 0) {
                var grade = {
                    "name": per_front,
                    "parent": null,
                    "children": []
                }
                TreeData[i].children[line] = grade;
                var per_name = per_back.split("、"); 
                for (var k = 0; k < per_name.length; k++) {
                    var name = {
                        "name": per_name[k],
                        "parent": null,
                        "children": []
                    }
                    TreeData[i].children[line].children[k] = name;
                }
                line ++;
            } else {
                var identity_name = {
                    "name": per_front,
                    "parent": null,
                    "children": []
                }
                TreeData[i] = identity_name;
                var per_identity = per_back.split("、"); 
                for (var k = 0; k < per_identity.length; k++) {
                    var identity = {
                        "name": per_identity[k],
                        "parent": null,
                        "children": []
                    }
                    TreeData[i].children[k] = identity;
                }

            }

        }
        /*********check*********/
        for (n = 0; n < i; n++) {
            check(TreeData[n], TreeData[i].name, TreeData[i]);
        }
        if (flag) {
            TreeData[i] = [];
            hide[h] = i;
            h++;
        }
        flag = 0;
        /*********check*********/

    }
    /*********make*********/
    var flag_hide = 0;
    //alert(count_tree);
    for (i = 0; i < count_tree; i++) {
        //alert(hide);
        for(j = 0; j < h; j ++){
            if(hide[j] == i) {
                flag_hide = 1;
            }
        }
        if(!flag_hide) MakeTreeGraph(i);
        flag_hide = 0;
    }
    /*********make*********/
}

check 函数用于遍历之前所有树的子节点,与所要查找的名字比较,若相同则是关联树,该节点添加到之前的树上。
值得注意的是在 GetData 中调用 check 的位置。若有关联树被并为子树,则原来的树被赋为空数组并隐藏,最终的 web 页面不会显示。

/**
 * 遍历之前所有树的子节点,与所要查找的名字比较,若相同则并为子树
 * @param tree json树
 * @param name 需遍历寻查找的名字
 * @param node 若遍历到相同的学生名字则成为子节点
 * @return 1 是关联树
 *         0 不是关联树
*/
function check(tree, name, node) {
    var length = 0;
    for (var i in tree.children) {
        length++;
    }
    for (var i = 0; i < length; i++) {
        if (tree.children[i].name == name) {
            flag = 1;
            tree.children[i] = node; 
            return 1;
        } else {
            check(tree.children[i], name, node);
        }
    }
    return 0;
}

四、附加特点设计与展示

1.可以将生成的学术家族树作为图片下载下来

核心代码:

使用 html2canvas 将 div 内容写入 Canvas 生成图片,再用 FileSaver.js 进行下载。

        function convert() {
            html2canvas(document.querySelector("#show"),{
                scrollY: -330, 
                scrollX: 0,
                useCORS: true
        }).then(canvas => {
                canvas.toBlob(function(blob) {
                    saveAs(blob, "Tree.png");
                });
                //document.body.appendChild(canvas);
            });
        };

效果展示:

2.输入下一次数据时无需刷新页面,直接在文本框输入即可自动清除之前的树

核心代码:

在处理新的数据前加入:

document.getElementById('show-tree').innerHTML = '';

效果展示:

五、目录结构和使用说明

  ├──css
  │   └──style.css
  ├──img
  │   └──bkg.jpg
  ├──js
  │   ├──graphing.js
  │   └──d3.v3.min.js    	
  └──index.html

下载后使用 Google Chrome 运行 index.html 即可展现预期结果。

六、单元测试

测试工具: mocha

Mocha是现在最流行的 JavaScript 测试框架之一,在浏览器和 Node 环境都可以使用。

安装环境: node.js

文件配置:

安装后配置单元测试的文件。在其目录下建立 datatest 文件夹,把要测试的 GetData.js 文件放进去。

如何运行:

在测试文件同目录下新建一个 GetData.test.js 然后就可以写单元测试代码啦!使用命令行进入目录,输入mocha GetData.test.js命令即可。

单元测试代码与运行结果:

部分代码如图,用于测试 GetData() 函数。

构造测试数据的思路:

根据测试时输入数据可能出现的各种异常情况编写代码,比如空格、空行、输入为空、输入导师时忘了写前面的部分等等。
如何考虑将来测试人员的刁难?那就……测出什么 bug 改什么 bug ……

七、多组数据测试

测试数据1:

导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

测试数据2:

导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

刘六:JAVA、数学建模

李二:字节跳动、京东云

测试数据3:

导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

导师:天一
2016级博士生:小方、小二、小吴
2015级硕士生:小李、小王、小许
2016级硕士生:小刘、小刚、小红
2017级本科生:小六、小丽、小七

测试数据4:

导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

刘六:JAVA、数学建模

李二:字节跳动、京东云

导师:天一
2018级博士生:小方、小二、小吴
2017级硕士生:小李、小王、小许
2018级硕士生:小刘、小刚、小红
2019级本科生:小六、小丽、小七

测试数据5:

导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

刘六:JAVA、数学建模

李二:字节跳动、京东云

导师:天一
2018级博士生:小方、小二、小吴
2017级硕士生:小李、小王、小许
2018级硕士生:小刘、小刚、小红
2019级本科生:小六、小丽、小七

导师:大明
2018级博士生:大方、大二、大吴
2017级硕士生:大李、大王、大许
2018级硕士生:大刘、大刚、大红
2019级本科生:大六、大丽、大七

导师:阿明
2018级博士生:阿方、阿二、阿吴
2017级硕士生:阿李、阿王、阿许
2018级硕士生:阿刘、阿刚、阿红
2019级本科生:阿六、阿丽、阿七

八、Github 的代码签入记录

九、遇到的代码模块异常或结对困难及解决方法

  1. 问题描述:关联树被并为子树后,怎样隐藏原来的树?
    做过哪些尝试:用了一个数组,专门用来记录这些不需要显示的树
    是否解决:已解决
    有何收获:对树形结构以及关联树有了不一样的理解

  2. 问题描述:绘制图时线条不听使唤
    做过哪些尝试:网上寻找了很多可视化树型结构的代码,都大同小异……
    是否解决:未解决
    有何收获:学习了 d3.js 这个可视化库,新技能 get,虽然线条还是丑

十、评价你的队友

郭盈江:我的队友做事条理且有责任心。我们在组队完成任务的过程中,相互鞭策、相互进步。感谢我的队友!美中不足的是我们两个都有拖延症,总是拖到最后,ε=(´ο`*)))唉。
刘国强:我的队友是一个很认真的人,我们配合还算默契、分工也较为明确,即使有一些分歧的时候她也能够很耐心地和我去磨合。通过这次作业我也学到了不少知识。希望下次可以克服拖延症。

原文地址:https://www.cnblogs.com/AJiang0704/p/13802771.html