项目链接:https://gitee.com/xyjtysk/quotationTools
在【QuotationTool的代码实现】主要数据结构.md中,我们介绍了如何针对的本项目的主要数据结构是什么,也就是把Excel表格里面的数据读出来,放到什么样的结构里面方便后续的处理。
下面我们接着介绍如何实现明细清单页。
要实现完成的流程,我们首先得获得Excel所在的完整路径+文件名,以及对输出的表格有那些列进行限定。
获得输入Excel路径
outputfileModelClass.py就是用来获得输入的Excel表格路径以及文件名的。
路径名可以通过配置文件设定,而文件名的话,需要遍历当前路径,从中找出Excel文件。
-
获得路径
首先我们在configure.conf里面设定inputfilePath和outputfilePath
# #*****************文件路径的配置*****************
[path]
# inputfilePath=project
inputfilePath=
# inputfilePath=input
outputfilePath=project
然后使用getParser进行解析,获得inputfilePath的具体值。
为了程序的可移值性,我们希望使用相对路径
那在python中怎么使用相对路径呢?
可以
filePath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))) ,filePath)
使用os.path.dirname(__file__)
获得这个py文件的当前路径,然后往上翻两层即可到达主目录。
- 遍历下面的所有文件, 找出Excel格式的文件。
files = [file for file in os.listdir(filePath) if file.rpartition('.')[2] == 'xls' or file.rpartition('.')[2]=='xlsx'];
这是一个列表生成式,大意是使用os.listdir(filePath)
列出所有的文件,然后找后缀名为"xls"或者"xlsx"的。
- 有一个就直接选中
elif len(files) == 1:
fileSelected = files[0];
如果有多个Excel文件怎么办?我们可以将输出的文件名尾巴上添一个数字,每运行一次,这个数字就+1,所以找尾号数字最大的就可以了。
这就要求,**我们在同一个目录里面最多只能放同一个Excel运行出来的文档。**
max = -1;
# 存放选中的文件名
fileSelected = ""
try:
for file in files :
# 去掉后缀,并且依照"-"分割为数组
fileSplitList = file.rpartition('.')[0].split('_');
# 此时如果后缀编号是8位,表明是从NHCT导出的,不选
if len(fileSplitList[-1] ) == 8:
max = -1;
# 如果倒数第一位比max还大,则更新
elif int(fileSplitList[-1]) > max:
max = int(fileSplitList[-1]);
fileSelected = file;
就是经典的找max的算法。
image.png
获得输出Excel路径
在上一章我们获得输入Excel的路径以及文件名,本章要获得输出的Excel完整路径,对应的文件为outputfileModelClass.py
之前已经说过,我们对同一个文档运行多次程序,每次的尾号要+1.
class outputfileModel(object):
def getOutputFile(self,file):
fileSplitList = file.rpartition('.')[0].split('_');
pattern = getParser('inOutmode','outputMode');
suffix = 0;
if len(fileSplitList[-1]) >= 3 :
suffix = 1;
else:
try :
suffix = int(fileSplitList[-1]) + 1;
except Exception as data:
print ("文件名不对"%data);
return fileSplitList[0] + "_" + pattern + "_" + str(suffix)+".xls";
-
首先对文件名使用"_"进行分割,切分成数组。
-
对数组的倒数第一位进行判断,如果是位数大于3(NHCT导出来的后缀都是日期,比如20180221,位数一定大于3),则将后缀设为1.
-
其他的后缀依次加一即可。
获得输入和输出的列的key
本功能由parameterModelClass.py
实现。
我们可以对输入输出的列对应的key进行设定,这样就可以自由的操纵哪些列输出,哪些列不输出了。
输入有哪些列需要和读入的Excel想匹配,如果不匹配的话,可能程序就会报错。
按照场景分类
我们可以归纳一下常用的几种表格模式:
-
给内部使用的,特点是有目录价
-
发给客户的,这就没有目录价了。
-
还有就是招商银行、平安科技投标
其实输入的文档一般都是从NHCT导出来的Excel,或者说经过脚本处理过的格式。他们的区别在于,脚本处理过的Excel多了一个单套数量。
image.png那么我们使用哪种数据结构来表示每一列对应的key呢?
可以选用数组,也可以使用dict
使用dict的好处在于,更为直观,如下
exportInput = {
"ID":"ID",
"BOM":"产品编码",
"typeID":"产品型号",
"description":"项目名称",
"quantity":"单套数量",
"unitsNetListPrice":"目录价",
"discount":"折扣",
"unitsNetPrice":"单价",
"totalPrice":"总价",
"totalListPrice":"总目录价",
"remarks":"备注",
"PL":"产线"
# "waston":"WATSON_LINE_ITEM_ID",
}
它表示从NHCT导出来的表格的列的含义。
同理可以得到脚本运行的出来的列
internalInput = {
"ID":"ID",
"BOM":"产品编码",
"typeID":"产品型号",
"description":"项目名称",
"quantity":"单套数量",
"totalQuantity":"总数量",
"unitsNetListPrice":"目录价",
"discount":"折扣",
"unitsNetPrice":"单价",
"totalPrice":"总价",
"remarks":"备注"
}
同样的道理,输出的列为
internalOutput = {
"ID":"ID",
"BOM":"产品编码",
"typeID":"产品型号",
"description":"项目名称",
"quantity":"单套数量",
"totalQuantity":"总数量",
"unitsNetListPrice":"目录价",
"discount":"折扣",
"unitsNetPrice":"单价",
"totalPrice":"总价",
"remarks":"备注"
}
image.png
把变量单独拆分
输入哪些列,输出哪些列,我们可能会经常修改,所以单独把要经常变的变量剥离出来,单独放在inputVariable.py
里面,这样就实现了配置和代码分离。
大家可以仔细观察一下上一小节提到的变量名,internalInput
、exportInput
,末尾都带了Input
这样,我们只需要把前缀与"input"联接在一起,就可以取出这个变量了。
读入输入输出的键值
最后我们只要使用函数把inputVariable.py
的变量读入即可,可见parameterModelClass.py
- 首先在配置文件里面设定输入和输出的模式
[inOutmode]
# 输入输出pattern
inputMode=internal
outputMode=internal
使用getParser读出来
inputPattern = getParser('inOutmode','inputMode');
info("输入的模式是"+str(inputPattern))
outputPattern = getParser('inOutmode','outputMode');
info("输出的模式是"+str(outputPattern))
-
输入变量可以使用
inputPattern+ "Input"
获得,输出变量可以使用outputPattern + "Output"
获得。然后就是动态的把
inputVariable.py
加载进来即可。
var = __import__("libs.inputVariable")
inputvar = getattr(var,"inputVariable")
inputParam = getattr(inputvar, (inputPattern+ "Input"));
outputParam = getattr(inputvar, (outputPattern + "Output"));
至此准备工作就做完了,下面就可以开始读Excel了。
image.png在Controller中调用
下面我们可以看看之前的几个Model是如何在Controller中进行调度的。
var = __import__("libs.inputVariable")
inputvar = getattr(var,"inputVariable")
inputFile = M("file").getProjectName();
outputFile = M("outputfile").getOutputFile(inputFile);
info("打开的文件是" + inputFile);
# 获得输入和输出的keys
[inputParam , outputParam] = M("parameter").getParameter(inputFile);
# 以quotationTools的根目录作为基准
basepath = os.path.dirname(os.path.dirname(os.path.dirname(__file__)));
inputPath = os.path.join(basepath,getParser('path','inputfilePath'),inputFile);
outputPath = os.path.join(basepath,getParser('path','outputfilePath'),outputFile);
# 主sheetName
sheetName = '价格明细清单';
网友评论