软工实践第一次个人编程作业

这个作业属于哪个课程 http://dwz.date/cts4
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2020/homework/11167
这个作业的目标 解析json 熟悉命令行操作 规范编程习惯 学习测试项目 github使用
学号 031802516
语言 python

PSP 表格

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

解题思路

刚开始看到题目的时候有点懵,看到控制台命令有点不知所措,在舍友的示范下很快就明白了。多次阅读题目后就有了比较清晰的思路,其实这题就是解析 json。但是由于要处理的数据量很大,所有要在初次访问的时候就把数据保存在本地起到缓存作用,并且在读文件时也采用readline() 防止内存炸掉。然后就开始复习了 json 的格式、python里一些数据结构之间的转换以及正则表达式的规则,解析完json就是完成了内核的,但是要编写命令提供交互,然后又去查询了有关py命令行参数编写的方法。

设计实现过程

代码组织

抓住这个编程题的核心就是解析json, 于是就先编写解析一个json文件的函数JsonAnalyse() 提取有用的信息, 在把JsonAnalyse 函数装进另一个寻找json文件的函数AnalyseAll(),实现对目标目录下所有的json文件的解析。把这些函数和相关的数据分装在类 Data里,在初始化init 的时候调用这些函数,解析并把所的数据以json的格式(解析采用 正则表达式 )存在本地,在后续的查询时直接调用本地文件,起到缓存的作用,最后再编写下命令行接口就好了。

核心代码init函数流程图:

代码说明

思路

先编写好解析一个json文件的函数,把要用的信息提取(采用正则表达式法 )出来并保存在类里定义的变量里,再把这个函数装进一个搜索文件夹中存在.json文件的函数就形成了一个解析一个文件夹的函数,最后配合一个把数据保存本地的函数,在初始化(构造)结束时就完成了所有解析。二次访问的时候就是把保存的文件提取出来,进行查询操作(查询函数过于简单就不再贴出来了,本质就是 json 和 dict 之间的转换)。整体上来说就是从核心突破,化繁为简,完成核心功能函数再一步步拓展,就可以完成整个的项目。

TotalAnalyse 函数

def TotalAnalyse(self,addr:str):#解析文件夹中的所有json
        for root, dic, files in os.walk(addr):
            for f in files:
                if f[-5:] == ".json":
                    jsonPath = f
                    self.JsonAnalyse(addr, jsonPath)        

 

JsonAnalyse 函数

    def JsonAnalyse(self, addr:str,jPath:str):#单个json解析函数
        f = open(addr + '\' + jPath, 'r', encoding='utf-8')
        try:
            while True:
                stmp = f.readline()
                if stmp:
                    pattern = re.compile('"login":".*?",')
                    res=pattern.search(stmp).group(0)
                    rkey1 = res[9:-2]

                    pattern = re.compile('"name":".*?",')
                    res = pattern.search(stmp).group(0)
                    rkey2 = res[8:-2]

                    pattern = re.compile('"type":".*?",')
                    res = pattern.search(stmp).group(0)
                    rkey3 = res[8:-2]

                    if not rkey3 in ['PushEvent','IssueCommentEvent',
                                            'IssuesEvent','PullRequestEvent']:
                        continue
                    if not rkey1 in self.uEvent.keys():
                        event = {'PushEvent':0,'IssueCommentEvent':0,
                                 'IssuesEvent':0,'PullRequestEvent':0}
                        self.uEvent[rkey1] = event

                    if not rkey2 in self.rEvent.keys():
                        event = {'PushEvent':0,'IssueCommentEvent':0,
                                 'IssuesEvent':0,'PullRequestEvent':0}
                        self.rEvent[rkey2] = event

                    if not rkey1+'&'+rkey2 in self.urEvent.keys():
                        event = {'PushEvent': 0, 'IssueCommentEvent': 0,
                                 'IssuesEvent': 0, 'PullRequestEvent': 0}
                        self.urEvent[rkey1 + '&' + rkey2] = event
                    self.uEvent[rkey1][rkey3] += 1
                    self.rEvent[rkey2][rkey3] += 1
                    self.urEvent[rkey1 + '&' + rkey2][rkey3] += 1
                else:
                    break
        except:
            pass
        finally:
            f.close()

SaveToLocal 函数

def SaveToLocal(self):
        try:
            with open('user.json', 'w', encoding = 'utf-8') as f:
                json.dump(self.uEvent, f)
            with open('repo.json', 'w', encoding = 'utf-8') as f:
                json.dump(self.rEvent, f)
            with open('userepo.json', 'w', encoding = 'utf-8') as f:
                json.dump(self.urEvent, f)
        except:
            raise RuntimeError("save error")
        finally:
            f.close()    

单元测试截图和描述

单元测试概述

分别对Data类测试初始化(包含了json 的读取、解析以及写入)、 三个查询(QueryByUser,QueryByRepo,QueryByUserAndRepo),在.json 文件插入几条特定的数据,在测试中采用断言的方式来判断是否成功完成对应的操作。一共4个test函数全部通过。

单元测试代码截图

单元测试运行截图

单元测试覆盖率

单元测试覆盖率优化和性能测试

性能测试提升

由于python 鸡肋的多线程,导致性能提升胎死腹中,不得不感叹一句 java 才是永远的神。
既然无法在多线程下手,就要从解析单个json突破。原来解析json模式为 读取文件->字符串->json -> json读取,改成了读取文件->字符串->正则表达式 模式。不仅可以加快索引的时间,也可以省区 str 与 dict 转换的时间。 经测试,在读取141MB文件时新模式用时1.29s,而旧模式用时2.19s ;在读取 1 GB 文件的情况下 新模式用时 5.36s 而旧模式用时 13.07s;可知,新模式的性能相对与旧模式提升了60%以上,并且读取的文件越大提升的性能就会越显著。在10GB 文件下,程序测试仍然会运行91.75s, 还是不没有达到理想要求。
值得一提的是,助教提供的程序有很多重复的json值访问,只要把这些值先预存起来也可以在一定程度上提高程序的性能。

  • 旧模式 VS 新模式(141MB文件)

  • 旧模式 VS 新模式(1GB文件)

性能测试截图

代码规范的链接

https://github.com/jieblue/2020-personal-python/blob/master/codestyle.md

总结

这次作业代码的两个核心内容解析json 和命令行参数编写,由于以前有做过解析json的作业,所以没有碰到太大的问题,命令行参数编写是第一次碰到,有去查阅资料,也参考了助教提供的模板,最后编写成功。还有也是第一册接触到单元测试和性能测试,也是查阅了一些资料(游走于csdn 和 cnblog之间)才完成这些测试。这次作业学到了很多新的知识,也复习了以前学的一些知识,收获很多。

原文地址:https://www.cnblogs.com/jieblue/p/13676464.html