统计太麻烦了,用python吧!

2018-03-05

如果回想起来应该是2015年9月的某个晚上桥梁馆101开完会之后就获得了一些每年必做的工作,人工做多了总想着自动化一些。

半年一度开学季

开学意味着要开始忙碌起来了,而一切忙碌的起始是统计班级同学返校、注册、思想动态等情况。 而需要进行八十多人的“庞大”班级的统计,哪怕只要统计一些简单的信息都要耗费颇多精力。开学统计,也只是其中一项而已。

幸好,逻辑够简单,那脚本也够简单,动起来。

要做些什么

我们要收集大家的信息,好比把大象塞进冰箱,一共分三步:

Step1. 写一个良好的excel模板,规规矩矩地写好例子以供大家照写不误,谁都喜欢无脑匹配不是么。

Step2. 发放模板给大家,并收集填写完成后的文件(最繁琐之步骤)

Step3. 写脚本跑脚本(最有趣的部分)

上代码

用python实现,方便快捷:

  • get_file_list(path),接收一个path参数,返回该路径下所有excel文件的文件名的集合
  • read_content(filepath, filename),返回目标excel文件的第一张表
  • write_content(wtable, target_row, rtable)向目标table中拷贝源rtable的特定行
  • assemble()则是业务逻辑,组织从分散的表到最终汇总表的映射关系
    import xlrd, xlwt, os

    class AssembleClient:

    def __init__(self, source_path, source_row, column_range, 
                target_file_name="result", target_sheet_name="sheet"):
        SUFFIX = ".xls"
        self.source_path = source_path
        self.source_row = source_row
        self.column_range = column_range
        self.target_file_name = target_file_name + SUFFIX
        self.target_sheet_name = target_sheet_name

    def assemble(self):
        target =  xlwt.Workbook()
        wtable = target.add_sheet(self.target_sheet_name, cell_overwrite_ok=True)
            
        index = 0
        for filename in self.get_file_list(self.source_path):
            rtable = self.read_content(self.source_path, filename)
            self.write_content(wtable, index, rtable)
            index += 1
                
        target.save(self.target_file_name)
        print("Completed!")

    def get_file_list(self, path):
        childs = os.listdir(path)

        result = []
        for a in childs:
            if a.endswith(".xlsx") or a.endswith(".xls"):
                result.append(a)
        return result

    def read_content(self, filepath, filename):
        fullpath = os.path.join('%s/%s' % (filepath, filename))
        source = xlrd.open_workbook(fullpath)
        rtable = source.sheets()[0]
        return rtable
            
    def write_content(self, wtable, target_row, rtable):

        if isinstance(self.column_range, int):
            column_range = list(range(0, self.column_range))
        elif isinstance(self.column_range, list):
            column_range = self.column_range

        target_index = 0
        for i in column_range:
            wtable.write(target_row, target_index, rtable.cell(self.source_row, i).value)
            target_index += 1
        return wtable

if __name__ == "__main__":

    SOURCE_PATH = './data'
    SOURCE_ROW = 3
    COLUMN_RANGE = 10

    ac = AssembleClient(SOURCE_PATH, SOURCE_ROW, COLUMN_RANGE, "result", "sheet")
    ac.assemble()

小结

目前来看,写得还不够成熟,很多地方也只是就事论事地解决了当下的问题,在以后的使用中继续refactor,不断完善。