提高效率,记一个内部工具的开发经历

简介: 提高效率,记一个内部工具的开发经历

一、开发介绍


1.开发背景

由于公司 V2项目 需要做组件化升级,但因为 V2项目 项目历史包袱大, 代码和文件非常多,而且嵌套较多,难以全面了解所需要调整的组件的影响范围,所以需要开发这么一个工具,来实现以下几个功能:

  • 需要能支持 自定义关键词检索 ,便于按不同的已有组件名搜索;
  • 需要能支持检索出该组件的 影响文件范围 ,还有 页面名称路由 等,便于测试按照页面快速测试;
  • 需要能支持 数据可视化 ,便于判断所有影响范围的权重;
  • 需要能 导出影响范围的路由文件必要数据

基于上面需求,我大概整理思路使用 NodejsPython 进行需求开发,原因有这几点:

  • 需求以操作文件为主,包括读写;
  • 需求对数据处理操作比较多,包括过滤,组装数据格式;
  • 需求对数据可视化的需求;

起初我准备只使用 Nodejs 完成这个需求,后面开发到一半,发现 数据可视化 方面,实在找不到一个满意的可视化插件,于是想到 Python 的一个2D绘图库—— Matplotlib ,使用起来非常方便,于是便选择了它。

这也是我用 Nodejs 做的第一个作品,还有很多优化空间,欢迎大佬指点哈,感激不尽。

2.工具文档

二、开发环境搭建

1.Nodejs环境搭建

对于 Nodejs 环境搭建,相信对于我们前端开发仔来说,应该是很简单,但这里考虑到可能原生的同学还不太清楚,这里我简单介绍:

  • 下载和安装 Nodejs

我们到 Nodejs官网 ,选择对应系统环境进行下载,然后直接打开安装。

  • 测试 Nodejs 环境

打开命令行工具,执行 node -v ,看是是否输出对应 Nodejs 版本号,我这显示:

v10.8.0

另外在 WIN7 系统下可能会出现下面报错,则需要将 nodejs 安装目录,添加全局路径:

node : 无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
  • 安装完成

2.Python环境搭建

  • 下载和安装 Python

Python官网 ,选择 3.x 版本下载(由于Python2.x版本已经停止维护,并且即将被淘汰),下载完成直接安装。

  • 测试 Python 环境

安装完成,打开命令行工具,执行 python ,看看输出结果是否是版本号和命令行交互模式,我这显示:

PS C:\Users\mi> python
Python 3.6.3 |Anaconda, Inc.| (default, Oct 15 2017, 03:27:45) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>
  • 安装绘图库 Matplotlib

python 安装其他包是用 pip install packageName 来安装,跟 Nodejs 中的 npm install packageName 是一样的,我们就这么安装 Matplotlib

pip install Matplotlib
  • 安装完成

三、开发过程

首先先介绍下开发的思路:

1.最终效果

最终我实现的效果是,开发 search_current_file.jssearch_current_file_python.py 两个文件,并通过执行两个命令,来获取对应数据文件:

  • 获取 所有包含关键词的文件的路径所在文件夹内文件数量所有文件对应页面的路由/参数/标题等数据 统计的文件和表格。
node search_current_file.js

  • 获取 所有文件夹中文件数量占总文件数的比例 的饼图结果。
python search_current_file_python.py

这里需要输入需要生成的指定文件夹的数据,默认不输入则生成所有文件夹下的数据。

2.Nodejs开发部分

首先定义几个下面主要使用的变量,其他没有写在这里的变量和作用,可以查看源码。

var Excel = require('exceljs');
var XLSX  = require('xlsx'); 
var filterFile = ['.html']; // 需要检索的文件类型
var filterDir  = ['lib'];   // 需要排除的文件夹
var classArray = [          // 需要检索的类名数组
    'search-holder','exe-bar-search','输入搜索内容','<exe-search','learn-search','ion-android-search'
];  
var resultArray    = [];    // 最终结果
var resultAlassify = {};    // 最终结果分类
var excelFileArr   = [];    // excel文件内容数组

2.1获取搜索结果

目的: 搜索包含关键词的所有HTML文件,并保存这些数据。

  • 核心方法 getCurrenAllFile()

我们通过 fs.readdir 方法,来获取路径下所有文件和文件夹名称作为一个集合;

然后遍历该集合,当 stat.isDirectory()true 则表示该结果为一个文件夹,为 false 则继续使用 getCurrenAllFile() 来读取下一层的文件信息。

/**
 * 获取当前项目的所有HTML文件
 * @param {string} paths 文件的路径
 */
var getCurrenAllFile = function (paths){
    // ... 省略部分
    var fileArr = [];// 初始化最终结果分类的对象
    fs.readdir(paths, function(err, files){
        _.forEach(files, function(item, index){
            var c_path = path.join(paths, item);
            var stat = fs.lstatSync(c_path);
            // TODO 关键
            if(stat && stat.isDirectory()){
                // .. 省略过滤文件夹的操作
                getCurrenAllFile(c_path);
                }
            }else{
                // .. 省略过滤文件夹的操作
                getCurrentFile(c_path, item);
            }
        });
    });
    return fileArr;
}
  • 核心方法 getCurrentFile()

读取每个文件的内容,然后再使用 searchCurrentFile() 方法去检索我们要搜索的关键词。

/**
 * 获取当前文件内容
 * @param {string} paths    文件的路径
 * @param {string} filename 文件名
 */
var getCurrentFile = function(paths, filename){
    fs.readFile(paths, 'utf8', function(err, data){
        // ... 省略部分
        if (err) console.log(err);
        searchCurrentFile(data, paths);
    });
};
  • 核心方法 searchCurrentFile()

这里遍历我们定义的 classArray 数组,这是包含我们所需要检索的所有关键词,如果检索结果为 true 则将结果保存到 resultArray 数组和 resultAlassify 数组。

/**
 * 检索当前文件内容
 * @param {object} data  文件的内容
 * @param {string} paths 文件的路径
 */
var searchCurrentFile = function(data, paths){
    _.forEach(classArray, function(val){
        // ... 省略部分
        if(data.indexOf(val) >= 0){
            resultArray.push(paths);
            resultAlassify[val].push(paths); // 保存最终结果(当前关键词下的对象)
        }
    }
};

2.2处理搜索结果

目的: 将获取到的数据,去重,格式化并保存成JSON,作为可视化的数据源。 这里有定义两个简单方法 unique() 用于数据去重,和 setEachDirFileNum() 统计文件数量,不做具体介绍。

这里我们使用 saveDataToJson() 将数据整理成 JSON 格式,并使用 setJSONFile() 方法,将JSON数据保存为 json 文件,用于可视化操作。

  • 核心方法 saveDataToJson()

这一步主要只用 loadsh 的分组函数 _.ground 来处理 JSON 数据,我们需要的格式是:

result = {
    template: [
        home:[ {}, {} ],
        my: [ {}, {} ]
        // ...
    ],
    view: [
        // ...
    ]
}

然后还需要处理成保存 Excel 时所需要的格式,再使用 setJSONFile() 方法保存 JSON 文件。

/**
 * 转成JSON数据,用来数据可视化
 * @param {*} data  需要处理的数据
 */
var saveDataToJson = function (data){
    var result = {};
    // 第一层分组 外层文件夹
    result = _.groupBy(data, function(item){
        item = item.replace(filePath+'\\','');
        var list = item.split('\\');
        return list[0];
    });
    // 第二层分组 内层文件夹
    for(var k in result){
        result[k] = _.groupBy(result[k], function(i){
            i = i.replace(filePath+'\\','');
            var r = i.split('\\');
            return r[1];
        });
    }
    for(var i in result){
        for(var m in result[i]){
            for(var n in result[i][m]){
                var currentPath = result[i][m][n].replace(filePath+'\\','');
                currentPath = currentPath.replace(/\\/g, '/');
                var current = excelFileObj[currentPath];
                result[i][m][n] = {
                    title : current ? current['路由名称'] : '该文件为模块',
                    path  : current ? current['文件路径'] : currentPath,
                    url   : current ? current['url'] : '该文件为模块',
                    params: current ? current['路由参数'] : '该文件为模块',
                    ctrl  : current ? current['控制器名称'] : '该文件为模块',
                    urls  : current ? current['url'] : '该文件为模块',
                };
            }
        }
    }
    setJSONFile(result);         // 保存JSON文件
};

2.3加入文件标题路由等数据

目的: 解析外部路由Excel表,合并到原有数据

  • 核心方法 getExcelFile()
    读取 Excel 数据并通过 resolve 返回。
/**
 * 读取Excel数据
 */
var getExcelFile = function(){
    return new Promise(function(resolve, reject){
        var excelPath = path.join(__dirname, excelReadName);
        fs.exists(excelPath, function(exists){
            if(exists){
                var workbook = XLSX.readFile(excelPath, {type: 'base64'});// 获取 Excel 中所有表名
                var sheetNames = workbook.SheetNames;
                resolve({workbook: workbook, sheetNames: sheetNames});
            }else{
                reject({message:'错误提示:请先获取路由列表文件!(执行node get_router.js)'});
            }
        });
    })
};
  • 核心方法 getEachSheet()

这里我们需要将 Excel 中的每个表的数据,都保存到 excelFileObj 中,另外需要注意,我们项目的 lodash 不能使用 4.0.0 以上版本的API。

/**
 * 解析Excel数据
 * @param {object} workbook    excel工作区数据
 * @param {object} sheetNames  excel工作表名数据
 */
var getEachSheet = function(workbook, sheetNames){
    _.forEach(sheetNames,function(item,index){
        var sheet = workbook.Sheets[sheetNames[index]];
        var json = XLSX.utils.sheet_to_json(sheet);  // 针对单个表,返回序列化json数据
        excelFileArr = excelFileArr.concat(json);    // 不能使用lodash的_.concat 因为lodash版本太低
    })
    _.forEach(excelFileArr, function(val, key){
        excelFileObj[val['文件路径']] = val;
    });
}

2.4生成结果文件

目的: 将处理后的结果生成对应的 Excel/JSON/TXT 文件:

这里生成 JSON/TXT 文件不做介绍,使用的是 Nodejs 内置的文件存储方法fs.write

  • 核心方法 setExcelFile()

主要是整理数据为保存 Excel 的数据格式。

/**
 * 保存Excel数据
 * @param {object} data  需要处理的数据
 * return excelFileName.xlsx
 */
var setExcelFile = function(data){
    var workbook = new Excel.Workbook();
    workbook.creator = 'EXE';
    workbook.lastModifiedBy = 'Leo';
    workbook.created     = new Date();
    workbook.modified    = new Date();
    workbook.lastPrinted = new Date();
    for(var item in data){    // 第一层循环 外层文件夹 templates views
        for(var list in data[item]){
            var worksheet = workbook.addWorksheet(list.toUpperCase()),
                rowData   = data[item][list];
            worksheet.columns = [
                { header: '页面标题'  , key: 'title' , width: 40 },
                { header: '文件路径'  , key: 'path'  , width: 60 },
                { header: '路由地址'  , key: 'url'   , width: 40 },
                { header: '路由参数'  , key: 'params', width: 40 },
                { header: '控制器名称', key: 'ctrl'  , width: 40 },
                { header: 'url'      , key: 'urls'  , width: 40 },
            ];
            for(var row in rowData){
                worksheet.addRow({
                    title : rowData[row].title,
                    path  : rowData[row].path,
                    url   : rowData[row].url,
                    params: rowData[row].params,
                    ctrl  : rowData[row].ctrl,
                    urls  : rowData[row].urls,
                }) 
            }
        }
    }
    workbook.xlsx.writeFile(path.join(__dirname, excelFileName)).then(function() {
        // ... 省略部分
    });
};

到这里我们 Nodejs 程序开发完成,我们最后会有一个文件 search_current_file_json.json 作为 Python 部分的数据源。

3.Python开发部分

Python 部分的内容相对比较简单,做的只有 加载数据简单处理数据可视化操作 三部分。

同样在刚开始部分,将几个重要的定义写一下:

# ... 省略一些
import matplotlib.pyplot as plt
keyName    = []  # 需要显示的分类图表(按外层文件夹)
selectName = ''  # 用户选择的文件夹名称

2.1读取数据源

我们通过使用 python 内置的 open 方法来读取文件,并导入内置方法 json 来读取前面 Nodejs 部分生成的 search_current_file_json.json 文件。

file = open('./search_current_file_json.json','r', encoding='utf-8')
file = json.load(file)

2.2设置命令行输入项

设置命令行输入项的目的是:让用户通过输入要查看的文件夹名称,来展示对应文件夹的饼图,默认显示所有文件夹饼图。

在设置之前,我们需要先通过 getKeyName() 方法获取到所有第一层文件夹的名称:

def getKeyName(): 
    for name in file: 
        keyName.append(name)

然后才能设置命令行输入项:

getKeyName()
select     = ','.join(keyName)
selectName = input('检索到的文件夹有:【' + select + '】,请输入要查看的文件夹名称(默认所有):')

2.3绘制单张饼图

接下来绘制单张饼图,这里主要就是设置饼图的参数:

  • 核心方法 drawOneChart()
def drawOneChart(name, label, data):
    plt_title = name
    plt.figure(figsize=(6,9)) # 调节图形大小
    labels = label   # 定义标签
    sizes  = data    # 每块值
    colors = [       # 每块颜色定义 这里省略掉
        #...
    ]
    explode = []    # 将某一块分割出来,值越大分割出的间隙越大
    max_data = max(sizes)
    for i in sizes: # 初始化每块之间间距,最大值分割出来
        if i == max_data:
            explode.append(0.2)
        else:
            explode.append(0)
    patches,text1,text2 = plt.pie(
        sizes, explode = explode, labels = labels, colors = colors,
        autopct = lambda pct: pctName(pct, data),  # 数值保留固定小数位
        frame   = 1,             # 是否显示饼图的图框,这里设置显示
        shadow  = True,          # 无阴影设置
        labeldistance = 1.1,     # 图例距圆心半径倍距离
        counterclock  = False,   # 是否让饼图按逆时针顺序呈现;
        startangle    = 90,      # 逆时针起始角度设置
        pctdistance   = 0.6      # 数值距圆心半径倍数距离
    )       
    plt.xticks(())
    plt.yticks(())
    plt.axis('equal')
    plt.legend()
    plt.title(plt_title+'文件夹下文件分布(顺时针)', bbox={'facecolor':'0.8', 'pad':5})
    plt.savefig(plt_title+'_'+saveImgName) # 一定放在plt.show()之前
    plt.show()

2.4绘制多张饼图

最后通过循环调用 drawOneChart() 来生成所有的饼图:

  • 核心方法 drawAllChart()

这个方法中需要对之前 JSON 数据再处理,将每个文件夹中文件数量作为饼图的数据,也就是这里的 values 的值。

def drawAllChart(openName):
    for name in keyName:
        labels = []
        values = []
        for view_name in file[name]:
            labels.append(view_name)
            values.append(len(file[name][view_name]))
        if openName == '' or openName == name:  
            drawOneChart(name, labels, values)
        else:
            print('输入有误')


四、总结

1.Nodejs知识点

这部分用得比较多的是 Nodejs 中的:

  • 文件读/写操作
  • 正则匹配操作
  • 数据格式处理操作

因此为了以后开发类似或者其他类型工具,还是需要加强这三方面的知识,这部分的代码可能不够简洁,代码也不够美观,但毕竟作为自己的经验积累,对这类工具开发会有更加清晰的思路。

2.Python知识点

这部分用得比较多的,其实是 Python 中的一些基础语法,这部分代码,其实也是加深自己对 Python 基础语法的使用和理解,练习操作。

3.拓展

接下来会找时间,优化项目代码,然后改造这个项目,将使用 Nodejs 和 Python 分别单独开发一套,并比较两者差距(执行效率/代码量)。 另外 Nodejs 的绘图库还有: node-echartsd3-node


目录
相关文章
|
8天前
|
敏捷开发 人工智能 搜索推荐
效率管理软件如何改变工作方式?全面介绍与应用场景
本文深入探讨了效率管理工具的定义、发展历程、功能特点及类型分类,帮助职场人士和团队更好地理解和选择这些工具,以提升整体工作效率。从个人任务管理到团队协作,效率管理工具通过优化工作流程、简化操作步骤和提升沟通效率,为现代工作方式提供了坚实支撑。
|
2月前
|
存储 数据可视化 数据库
团队文档管理有困难?总有一款工具合适你
本文介绍了团队文档管理的重要性及其在提升工作效率、保障协同作业和知识传承中的关键作用。随后,详细评述了六款广受好评的团队文档管理工具:板栗看板、Notion、Confluence、Quip、Google Workspace 和 Microsoft 365,分别从功能类型、发展历程、价格费用、产品特色、优缺点、适用场景及应用案例等方面进行了对比分析,旨在帮助读者根据自身需求选择最合适的工具。
团队文档管理有困难?总有一款工具合适你
|
3月前
|
关系型数据库 Java 数据库
探索后端技术:构建高效系统的秘诀
在当今信息技术飞速发展的时代,后端开发作为软件架构中的核心部分,其重要性不言而喻。本文将深入探讨后端技术的各个方面,从基础概念到高级应用,帮助读者全面了解后端开发的世界,揭示构建高效系统的秘诀。
|
3月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
218 3
|
算法 Java Shell
简化Java编程的法宝,让工作更高效
简化Java编程的法宝,让工作更高效
相亲软件开发,关注应用启动优化的本质
相亲软件开发,关注应用启动优化的本质
20 种高效的工作方式,助力你更有效率的编码
20 种高效的工作方式,助力你更有效率的编码
916 0
|
Java 测试技术 Android开发
让测试工作为项目运行保驾护航 | 带你学《Java面向对象编程》之一百
本节为大家介绍了用例测试工具JUnit,并详细介绍了JUnit组件包的导入方法以及编写JUnit程序测试类进行项目测试。
 让测试工作为项目运行保驾护航 | 带你学《Java面向对象编程》之一百
|
测试技术 持续交付 调度
如何快速掌握阿里巴巴内部高效测试流程?
阿里巴巴分层自动化从自动化成本和效果这两个重要缺点上突破,进行分层自动化工具和项目流程的双重革命,最终达到业内领先的研发测试比。
8532 1
让工作变得高效而简单的10种方法
导读:你是不是从早忙到晚,感觉自己一直被工作追着跑?但你的忙乱也许不是因为工作太多,而是因为没有重点,目标不清楚,所以才让工作变得越来越复杂,时间越来越不够用。一天只有1440分钟,在信息庞杂、速度加快的职场环境里,我们必须在越来越少的时间内,完成越来越多的事情。
1017 0
下一篇
DataWorks