|
|
- #! python
- # -*- coding:utf-8 -*-
-
- import json
- import openpyxl
- import os
- import re
- import sys
-
- import color_print
- from slpp.slpp import slpp as lua
-
- # array数组模式下,各个栏的分布 第一行行是文档注释,不导出
- ACMT_ROW = 2 # comment row 注释
- ATPE_ROW = 3 # type row 类型
- ASRV_ROW = 4 # server row 服务器
- ACLT_ROW = 5 # client row 客户端
- AKEY_COL = 1 # key column key所在列
-
- # object(kv)模式下,各个栏的分布 第一行是文档注释,不导出
- OCMT_COL = 1 # comment column 注释
- OTPE_COL = 2 # type column 类型
- OSRV_COL = 3 # server column 服务器
- OCLT_COL = 4 # client column 客户端
- OCTX_COL = 5 # content column 内容所在列
- OFLG_ROW = 2 # flag object模式下 server client所在行
-
- SHEET_FLAG_ROW = 2 # 2列1行表示是array 还是object 或者是不用导出的表
- SHEET_FLAG_COL = 1 # 2列1行表示是array 还是object 或者是不用导出的表
-
- # 导出服务器/客户端配置标识
- SRV_FLAG = "server"
- CLT_FLAG = "client"
-
- # array数组模式下 是否导出 服务器/客户端 get_list()函数
- ALIST_ROW = 3 # 是否导出get_list()函数的设置行
- ALIST_COL = 1
- SRV_LIST = "slist"
- CLT_LIST = "clist"
-
- # 用于标识表的类型 不是这两种就不会导出数据
- ARRAY_FLAG = "array" # 标识表的是array类型的表
- OBJECT_FLAG = "object" # 标识表的是object类型的表
-
- KEY_FLAG = "$key_" # array表的 作为Key字段的前缀标识
- KEY_FLAG_LEN = 5 # array表的 作为Key字段的前缀标识长度 用于分割字符串
-
- # 支持的数据类型
- # "int":1,"number":2,"int64":3,"string":4, "tuple":5, "list":6, "dict":7 为python原生数据格式
- # "json":8,"lua":9 为json和lua的数据格式
- # excel配置的时候 对应类型字段 填入对应数据类型的有效数据格式的数据就OK
- TYPES = {"int": 1, "number": 2, "int64": 3, "string": 4, "tuple": 5, "list": 6, "dict": 7, "json": 8, "lua": 9}
-
- try:
- basestring
- except NameError:
- basestring = str
-
- # python3中没有uincode了,int能表示int64
- try:
- long
- except NameError:
- long = int
-
- # python3中没有unicode了
- try:
- unicode
- except NameError:
- unicode = str
-
-
- # 类型转换器
- class ValueConverter(object):
- def __init__(self):
- pass
-
- # 在python中,字符串和unicode是不一样的。默认从excel读取的数据都是unicode。
- # str可以通过decode转换为unicode
- # ascii' codec can't encode characters
- # 这个函数在python3中没用
- def to_unicode_str(self, val):
- if isinstance(val, str):
- return val
- elif isinstance(val, unicode):
- return val
- else:
- return str(val).decode("utf8")
-
- def to_value(self, val_type, val):
- if "int" == val_type:
- return int(val)
- elif "int64" == val_type:
- return long(val)
- elif "number" == val_type:
- # 去除带小数时的小数点,100.0 ==>> 100
- # number就让它带小数点吧,不然强类型的配置无法正确识别出来
- # if long( val ) == float( val ) : return long( val )
- return float(val)
- elif "string" == val_type:
- return self.to_unicode_str(val)
- elif "json" == val_type:
- return json.loads(val)
- elif "lua" == val_type:
- return lua.decode(val)
- elif "tuple" == val_type:
- return tuple(eval(val))
- elif "list" == val_type:
- return list(eval(val))
- elif "dict" == val_type:
- return dict(eval(val))
- else:
- self.raise_error("invalid type", value)
-
-
- class Sheet(object):
-
- def __init__(self, base_name, wb_sheet, srv_writer, clt_writer, srv_is_list, clt_is_list):
-
- self.types = [] # 记录各列字段的类型
-
- self.srv_writer = srv_writer
- self.clt_writer = clt_writer
-
- self.srv_fields = [] # 服务端各列字段名
- self.clt_fields = [] # 客户端各列字段名
- self.srv_comment = {} # 服务器字段注释
- self.clt_comment = {} # 客户端字段注释
-
- self.srv_keys = {} # 服务器key
- self.clt_keys = {} # 客户端key
-
- self.srv_is_list = srv_is_list # 客户端是否导致get_list() 列表函数
- self.clt_is_list = clt_is_list # 客户端是否导致get_list() 列表函数
-
- self.converter = ValueConverter()
-
- self.wb_sheet = wb_sheet
- self.base_name = base_name
-
- match_list = re.findall("[a-zA-Z0-9_]+$", base_name)
- if None == match_list or 1 != len(match_list):
- Exception(base_name, "not a legal file name")
-
- self.base_file_name = match_list[0]
-
- # 记录出错时的行列,方便策划定位问题
- self.error_row = 0
- self.error_col = 0
-
- # 记录出错位置
- def mark_error_pos(self, row, col):
- if row > 0: self.error_row = row
- if col > 0: self.error_col = col
-
- # 发起一个解析错误
- def raise_error(self, what, val):
- excel_info = format("DOC:%s,SHEET:%s,ROW:%d,COLUMN:%d" % \
- (self.base_name, self.wb_sheet.title, self.error_row, self.error_col))
- raise Exception(what, val, excel_info)
-
- def to_value(self, val_type, val):
- try:
- return self.converter.to_value(val_type, val)
- except Exception:
- t, e = sys.exc_info()[:2]
- self.raise_error("ConverError", e)
-
- # 解析一个表格
- def decode_sheet(self):
- wb_sheet = self.wb_sheet
-
- self.decode_type()
- self.decode_field()
- self.decode_ctx()
-
- color_print.printGreen(" covert successfully... sheet name -> %s \n" % wb_sheet.title)
- return True
-
- # 写入配置到文件
- def write_one_file(self, ctx, base_path, writer, keys_list, comment_text, is_list):
- # 有些配置可能只导出客户端或只导出服务器
- if not any(ctx):
- return
-
- write_file_name = self.base_file_name
- if self.wb_sheet.title.find("+") >= 0 or self.wb_sheet.title.find("-") >= 0:
- match_list = re.findall("[a-zA-Z0-9_]+$", self.wb_sheet.title)
- if 1 == len(match_list):
- write_file_name = write_file_name + '_' + match_list[0]
-
- wt = writer(self.base_name, self.wb_sheet.title, write_file_name, keys_list, comment_text, is_list)
- ctx = wt.context(ctx)
- suffix = wt.suffix()
- if None != ctx:
- path = base_path + write_file_name + suffix
-
- # 必须为wb,不然无法写入utf-8
- file = open(path, 'wb')
- file.write(ctx.encode("utf-8"))
- file.close()
-
- # 分别写入到服务端、客户端的配置文件
- def write_files(self, srv_path, clt_path):
- if None != srv_path and None != self.srv_writer:
- self.write_one_file(self.srv_ctx, srv_path, self.srv_writer, self.srv_keys, self.srv_comment,
- self.srv_is_list)
- if None != clt_path and None != self.clt_writer:
- self.write_one_file(self.clt_ctx, clt_path, self.clt_writer, self.clt_keys, self.clt_comment,
- self.clt_is_list)
-
-
- # 导出数组类型配置,A1格子的内容有array标识
- class ArraySheet(Sheet):
-
- def __init__(self, base_name, wb_sheet, srv_writer, clt_writer, srv_is_list, clt_is_list):
- # 记录导出各行的内容
- self.srv_ctx = []
- self.clt_ctx = []
-
- super(ArraySheet, self).__init__(
- base_name, wb_sheet, srv_writer, clt_writer, srv_is_list, clt_is_list)
-
- # 解析各列的类型(string、number...)
- def decode_type(self):
- # 第一列没数据,类型可以不填,默认为None,但是这里要占个位
- self.types.append(None)
-
- for col_idx in range(AKEY_COL + 1, self.wb_sheet.max_column + 1):
- self.mark_error_pos(ATPE_ROW, col_idx)
- value = self.wb_sheet.cell(row=ATPE_ROW, column=col_idx).value
-
- # 单元格为空的时候,wb_sheet.cell(row=1, column=2).value == None
- # 类型那一行必须连续,空白表示后面的数据都不导出了
- if value == None:
- break
- if value not in TYPES:
- self.raise_error("invalid type", value)
-
- self.types.append(value)
-
- # 解析客户端、服务器的字段名(server、client)那两行
- def decode_one_field(self, fields, row_index, keys):
- key_index = 1
- for col_index in range(AKEY_COL, len(self.types) + 1):
- value = self.wb_sheet.cell(
- row=row_index, column=col_index).value
- # 对于array类型的表 一条数据可以有多个值作为KEY 需要处理一下
- if None != value and value.find(KEY_FLAG) >= 0:
- value = value[KEY_FLAG_LEN:]
- keys[value] = key_index
- key_index = key_index + 1
-
- # 对于不需要导出的field,可以为空。即value为None
- fields.append(value)
-
- # 解析注释那一行
- def decode_one_comment(self, fields, row_index, key_index):
- for col_index in range(AKEY_COL + 1, len(self.types) + 1):
- value = self.wb_sheet.cell(
- row=row_index, column=col_index).value
-
- key = self.wb_sheet.cell(
- row=key_index, column=col_index).value
-
- if None == key:
- continue
-
- # 对于不需要导出的field,可以为空。即value为None
- if key.find(KEY_FLAG) >= 0:
- key = key[KEY_FLAG_LEN:]
- fields[key] = value
- else:
- fields[key] = value
-
- # 导出客户端、服务端字段名(server、client)那一列
- def decode_field(self):
- self.decode_one_field(self.srv_fields, ASRV_ROW, self.srv_keys)
- self.decode_one_field(self.clt_fields, ACLT_ROW, self.clt_keys)
- # 导出服务器和客户端对用字段的注释, 导表的时候可能用到
- self.decode_one_comment(self.srv_comment, ACMT_ROW, ASRV_ROW)
- self.decode_one_comment(self.clt_comment, ACMT_ROW, ACLT_ROW)
-
- # 解析出一个格子的内容
- def decode_cell(self, row_idx, col_idx):
- value = self.wb_sheet.cell(row=row_idx, column=col_idx).value
- if None == value:
- return None
-
- # 类型是从0下标开始,但是excel的第一列从1开始
- self.mark_error_pos(row_idx, col_idx)
- return self.to_value(self.types[col_idx - 1], value)
-
- # 解析出一行的内容
- def decode_row(self, row_idx):
- srv_row = {}
- clt_row = {}
-
- # 第一列没数据,从第二列开始解析
- for col_idx in range(AKEY_COL + 1, len(self.types) + 1):
- value = self.decode_cell(row_idx, col_idx)
- if None == value: continue
-
- srv_key = self.srv_fields[col_idx - 1]
- clt_key = self.clt_fields[col_idx - 1]
-
- if srv_key:
- srv_row[srv_key] = value
- if clt_key:
- clt_row[clt_key] = value
- return srv_row, clt_row # 返回一个tuple
-
- # 解析导出的内容
- def decode_ctx(self):
- for row_idx in range(ACLT_ROW + 1, self.wb_sheet.max_row + 1):
- srv_row, clt_row = self.decode_row(row_idx)
-
- # 不为空才追加
- if any(srv_row):
- self.srv_ctx.append(srv_row)
- if any(clt_row):
- self.clt_ctx.append(clt_row)
-
-
- # 导出object类型的结构,A1格子有object标识
- class ObjectSheet(Sheet):
-
- def __init__(self, base_name, wb_sheet, srv_writer, clt_writer, srv_is_list, clt_is_list):
- # 记录导出各行的内容
- self.srv_ctx = {}
- self.clt_ctx = {}
-
- super(ObjectSheet, self).__init__(
- base_name, wb_sheet, srv_writer, clt_writer, srv_is_list, clt_is_list)
-
- # 解析各字段的类型
- def decode_type(self):
- for row_idx in range(OFLG_ROW + 1, self.wb_sheet.max_row + 1):
- self.mark_error_pos(row_idx, OTPE_COL)
- value = self.wb_sheet.cell(row=row_idx, column=OTPE_COL).value
-
- # 类型必须连续,遇到空则认为后续数据不再导出
- if value == None:
- break
- if value not in TYPES:
- self.raise_error("invalid type", value)
-
- self.types.append(value)
-
- # 导出客户端、服务端字段名(server、client)那一列
- def decode_one_field(self, fields, col_idx):
- for row_idx in range(OFLG_ROW + 1, len(self.types) + OFLG_ROW + 1):
- value = self.wb_sheet.cell(row=row_idx, column=col_idx).value
-
- # 对于不需要导出的field,可以为空。即value为None
- fields.append(value)
-
- # 解析注释那一行
- def decode_one_comment(self, fields, col_idx, key_index):
- for row_idx in range(OFLG_ROW + 1, len(self.types) + OFLG_ROW + 1):
- value = self.wb_sheet.cell(row=row_idx, column=col_idx).value
-
- key = self.wb_sheet.cell(row=row_idx, column=key_index).value
-
- if None == value:
- continue
- # 导出存在值的fields的注释
- fields[key] = value
-
- # 导出客户端、服务端字段名(server、client)那一列
- def decode_field(self):
- self.decode_one_field(self.srv_fields, OSRV_COL)
- self.decode_one_field(self.clt_fields, OCLT_COL)
- # 导出服务器和客户端对用字段的注释, 导表的时候可能用到
- self.decode_one_comment(self.srv_comment, OCMT_COL, OSRV_COL)
- self.decode_one_comment(self.clt_comment, OCMT_COL, OCLT_COL)
-
- # 解析一个单元格内容
- def decode_cell(self, row_idx):
- value = self.wb_sheet.cell(row=row_idx, column=OCTX_COL).value
- if None == value: return None
-
- # 在object的结构中,数据是从第二行开始的,所以types的下标偏移2
- self.mark_error_pos(row_idx, OCTX_COL)
- return self.to_value(self.types[row_idx - OFLG_ROW - 1], value)
-
- # 解析表格的所有内容
- def decode_ctx(self):
- for row_idx in range(OFLG_ROW + 1, len(self.types) + OFLG_ROW + 1):
- value = self.decode_cell(row_idx)
-
- if None == value:
- continue
-
- srv_key = self.srv_fields[row_idx - OFLG_ROW - 1]
- clt_key = self.clt_fields[row_idx - OFLG_ROW - 1]
-
- if srv_key: self.srv_ctx[srv_key] = value
- if clt_key: self.clt_ctx[clt_key] = value
-
-
- class ExcelDoc:
-
- def __init__(self, file, abspath):
- self.file = file
- self.abspath = abspath
-
- # 是否需要解析
- # 返回解析的对象类型
- def need_decode(self, wb_sheet):
- sheet_val = wb_sheet.cell(
- row=SHEET_FLAG_ROW, column=SHEET_FLAG_COL).value
-
- sheeter = None
- srv_value = None
- clt_value = None
- srv_is_list = False
- clt_is_list = False
- if ARRAY_FLAG == sheet_val:
- if wb_sheet.max_row <= ACLT_ROW or wb_sheet.max_column <= AKEY_COL:
- return None
-
- sheeter = ArraySheet
- srv_value = wb_sheet.cell(row=ASRV_ROW, column=AKEY_COL).value
- clt_value = wb_sheet.cell(row=ACLT_ROW, column=AKEY_COL).value
-
- is_list_val = wb_sheet.cell(
- row=ALIST_ROW, column=ALIST_COL).value
- if None != is_list_val and is_list_val.find(SRV_LIST) >= 0:
- srv_is_list = True
- if None != is_list_val and is_list_val.find(CLT_LIST) >= 0:
- clt_is_list = True
-
- elif OBJECT_FLAG == sheet_val:
- sheeter = ObjectSheet
- srv_value = wb_sheet.cell(row=OFLG_ROW, column=OSRV_COL).value
- clt_value = wb_sheet.cell(row=OFLG_ROW, column=OCLT_COL).value
- else:
- return None, srv_is_list, clt_is_list
-
- # 没有这两个标识就不是配置表。可能是策划的一些备注说明
- if SRV_FLAG != srv_value or CLT_FLAG != clt_value:
- return None, srv_is_list, clt_is_list
- return sheeter, srv_is_list, clt_is_list
-
- def decode(self, srv_path, clt_path, srv_writer, clt_writer):
- color_print.printYellow(" start covert: %s \n" % self.file.ljust(44, "*"))
- base_name = os.path.splitext(self.file)[0]
- wb = openpyxl.load_workbook(self.abspath)
-
- for wb_sheet in wb.worksheets:
- Sheeter, srv_is_list, clt_is_list = self.need_decode(wb_sheet)
- if None == Sheeter:
- color_print.printPink(" covert skip........... sheet name -> %s\n" % wb_sheet.title)
- continue
-
- sheet = Sheeter(base_name, wb_sheet, srv_writer, clt_writer, srv_is_list, clt_is_list)
- if sheet.decode_sheet():
- sheet.write_files(srv_path, clt_path)
|