def copy_files(path_list, dest_path, skeep_exist=False): """ 对指定文件列表,复制到指定目录。 @param path_list: 文件路径列表 @param dest_path: 目标路径 @param skeep_exist: 跳过已经存在的文件 @return: 无 """ # 创建目标路径 if not os.path.exists(dest_path): os.makedirs(dest_path) # 复制文件 for src_file in path_list: if not os.path.isfile(src_file): logcm.print_info("%s not exist!" % src_file, fg='red') else: # 分离文件名和路径 src_path, src_name = os.path.split(src_file) # 目标路径 dest_file = os.path.join(dest_path, src_name) # 跳过已经存在的文件 if skeep_exist and os.path.exists(dest_file): continue # 复制文件 logcm.print_info("copy %s -> %s" % (src_file, dest_file)) shutil.copyfile(src_file, dest_file)
def save_str(content, encoding='utf-8', path=None, file_name=None): """ 保存字符串到文件 @param content: 字符串内容 @param encoding: 字符串编码 @param path: 文件路径 @param file_name: 文件名 @return: 无 """ # 文件名为空返回空 if file_name is None: logcm.print_info("File name is empty.", fg='red') return None # 路径不为空,路径拼接 if path is not None: makedir(path) file_path = os.path.join(path, file_name) else: makedir(file_name, by_file=True) file_path = file_name # 文件完整路径 f = codecs.open(file_path, 'w', encoding) f.write(content) f.close() logcm.print_info("Save str file finished. --> %s/%s" % (path, file_name))
def save_xml(xml_str, save_path, encoding="utf-8"): """ 格式化root转换为xml文件 @param xml_str: 根节点 @param save_path: xml文件 @param encoding: 文本编码 @return: 无 """ try: logcm.print_info("Saving xml file --> %s" % save_path) # 解析XML文档 xml_doc = minidom.parseString(xml_str.encode(encoding)) # 确保目录存在 filecm.makedir(save_path, by_file=True) # 写入XML文件 with open(save_path, "w+") as file: xml_doc.writexml(file, addindent="\t", newl="\n", encoding=encoding) except Exception as e: logcm.print_info("Saving xml error! %s" % e, fg='red')
def read_lines(path=None, file_name=None, encoding="utf-8"): """ 读取文件到字符串数组 @param path: 文件路径 @param file_name: 文件名 @param encoding: 字符编码 @return: 字符串数组 """ # 文件名为空返回空 if file_name is None: logcm.print_info("File name is empty.", fg='red') return None # 路径不为空,路径拼接 if path is not None: file_path = os.path.join(path, file_name) else: file_path = file_name # 文件不存在,返回空 if not os.path.exists(file_path): logcm.print_info("File not found. %s" % file_path, fg='red') return None # 读取文件 file = codecs.open(file_path, 'r', encoding) # 行字符串列表 lines = [line.strip() for line in file] file.close() return lines
def xml_to_dict(xml_bytes=None, xml_path=None): """ 把XML文本或文件转换成字典 :param xml_bytes: XML文本字节数组 :param xml_path: XML文件路径 """ if xml_path is not None: logcm.print_info("Reading xml file : %s" % xml_path) xml_bytes = filecm.read_bytes(file_name=xml_path) if xml_bytes is None: logcm.print_info("xml_bytes is None!", fg='red') return None root_node = etree.XML(xml_bytes) # 取得根节点下字典一览 root_list = [] node_to_list(root_node, root_list) # 根节点字典做成 tag_root = get_tag_name(root_node) # 节点转字典 data_root = node_to_dict(root_node, tag_root, root_list) # 命名空间 if root_node.nsmap: data_root[KEY_XMLNS] = dict(root_node.nsmap) return data_root
def save_tmp(img, func_key, deal_key, tmp_path=None, tmp_key="", img_list=None, title_list=None): """ 保存临时图片 @:param img 图片 @:param func_key 函数关键词 @:param deal_key 处理关键词 @:param tmp_path 临时目录 @:param tmp_key 临时关键词 @:param img_list 图片列表 @:param title_list 标题列表 @return: 无 """ if img is None: return None # 保存路径 if tmp_path is not None: save_path = "%s/%s-%s_%s.jpg" % (tmp_path, tmp_key, func_key, deal_key) logcm.print_info("Temp file save to %s" % save_path) cv2.imwrite(save_path, img) # 图片加入列表 if img_list is not None: img_list.append(img) # 标题加入列表 if title_list is not None: row_num, col_num = img.shape[:2] title = '%s %dx%d' % (deal_key, col_num, row_num) title_list.append(title)
def check_regex(obj, title="", pattern=None, show_error=True): """ 规则表达式校验 :param obj: 对象 :param title: 标题 :param pattern:规则表达式 :param show_error:是否显示错误 :return: 校验结果对象 """ if obj is None: return CheckResult(True) if not isinstance(obj, str): msg = "%s值为非字符串!" % title if show_error: logcm.print_info(msg, fg='red') return CheckResult(False, msg) if len(obj) == 0: return CheckResult(True) if pattern is not None and pattern in REGEX_MAP: ptn = re.compile(REGEX_MAP[pattern]) if not ptn.match(obj): msg = "%s值为%s,不符合要求的规则表达式:%s!" % (title, obj, pattern) if show_error: logcm.print_info(msg, fg='red') return CheckResult(False, msg) return CheckResult(True)
def save_data(data, path=None, file_name=None): """ 保存数据到本地文件 @param data: 数据内容 @param path: 文件路径 @param file_name: 文件名 @return: 无 """ # 文件名为空返回空 if file_name is None: logcm.print_info("File name is empty.", fg='red') return None # 路径不为空,路径拼接 if path is not None: makedir(path) file_path = os.path.join(path, file_name) else: makedir(file_name, by_file=True) file_path = file_name # 文件完整路径 with open(file_path, "wb") as file: file.write(data) logcm.print_info("Save data file finished. --> %s/%s" % (path, file_name))
def zip(img, zip_h=1.0, zip_v=1.0, save_path=None): """ 对原始图片按照指定倍数压缩后画布仍为原始大小 @param img: 原始图片或路径 @param zip_h: 水平缩小比例 @param zip_v: 竖直缩小比例 @param save_path: 保存路径(可选) @return: 压缩后的图片 """ if zip_h > 1.0 or zip_v > 1.0: logcm.print_info('Zip ratio must <= 1.0!', fg='red') # 取得图片 im = get_im(img) # 图片原始尺寸 (width, height) = im.size # 缩小图片 new_width = max(int(width * zip_h), 2) new_height = max(int(height * zip_v), 2) im = resize(im, new_width, new_height, keep_ratio=False) # 重设画布大小 im_zip = resize_canvas(im, width, height, 0) # 保存图片 if save_path is not None: im_zip.save(save_path) return im_zip
def setUp(self): """ 在执行每个测试用例之前被执行,任何异常(除了unittest.SkipTest和AssertionError异常以外) 都会当做是error而不是failure,且会终止当前测试用例的执行。 :return: """ logcm.print_info('setUp ...')
def save_tmp(img, func_key, deal_key, tmp_path=None, tmp_key="", img_list=None, title_list=None): """ 保存临时图片 @:param img 图片 @:param func_key 函数关键词 @:param deal_key 处理关键词 @:param tmp_path 临时目录 @:param tmp_key 临时关键词 @:param img_list 图片列表 @:param title_list 标题列表 @return: 无 """ # 保存路径 if tmp_path is not None: save_path = "%s/%s-%s_%s.jpg" % (tmp_path, tmp_key, func_key, deal_key) logcm.print_info("Temp file save to %s" % save_path) cv2.imwrite(save_path, img) # 图片加入列表 if img_list is not None: img_list.append(img) # 标题加入列表 if title_list is not None: title_list.append(deal_key)
def zoom_in(img, ratio=1.1, img_org=None, save_path=None): """ 对原始图片居中放大指定倍数后裁切为原始大小 @param img: 图片或路径 @param ratio: 放大比例 @param img_org: 原始图片或路径 @param save_path: 保存路径(可选) @return: 放大后的图片 """ if ratio < 1.0: logcm.print_info('Zoom in ratio must >= 1.0!', fg='red') # 取得图片 im = get_im(img) # 图片原始尺寸 (width, height) = im.size # 放大图片 new_width = int(width * ratio) new_height = int(height * ratio) # 如果存在原始图片,使用原始图片更清晰 if img_org is not None: im_org = get_im(img_org) im = resize(im_org, new_width, new_height) else: im = resize(im, new_width, new_height) # 计算居中剪切时,左上角坐标 x = int(math.floor((new_width - width) / 2)) y = int(math.floor((new_height - height) / 2)) # 剪切 im_zoom = im.crop((x, y, x + width, y + height)) # 保存图片 if save_path is not None: im_zoom.save(save_path) return im_zoom
def read_config_dict(self, config_file): """ 读取config文件 :param config_file:配置文件路径 :return: 配置字典 """ config_data = {} try: remote_file = self.sftp.open(config_file, 'r') # read any new lines from the file line = remote_file.readline() while line: line = line.strip('\r\n') # 排除注释行 if not line.startswith("#"): # 数据值 dataList = line.split("="); if len(dataList) == 2: key = dataList[0].strip() val = dataList[1].strip() config_data[key] = val line = remote_file.readline() remote_file.close() except Exception as e: logcm.print_info("read_config_dict Exception : %s" % e, fg='red') return config_data
def get_url(self, method, key=None, val=None): """ 根据当前方法,取得URL @param method: 取值范围:(add, load, update, del) @param key: 设置键 @param val: 设置值 @return: URL """ if method == 'add': url = "http://%s:%d/ops/zk?method=zkAdd&nameList=%s&envList=%s&inputKeyAdd=%s&inputValueAdd=%s" % ( self.cfg["ip"], self.cfg['port'], self.cfg['sys_name'], self.cfg['sys_env'], key, val) elif method == 'load': url = "http://%s:%d/ops/zk?method=zkLoad&nameList2=%s&envList2=%s&%d" % ( self.cfg["ip"], self.cfg['port'], self.cfg['sys_name'], self.cfg['sys_env'], time.time()) elif method == 'update': url = "http://%s:%d/ops/zk?method=zkSetData&nameList3=%s&envList3=%s&inputKey=%s&inputValue=%s" % ( self.cfg["ip"], self.cfg['port'], self.cfg['sys_name'], self.cfg['sys_env'], key, val) elif method == 'del': url = "http://%s:%d/ops/zk?method=zkDelData&nameList4=%s&envList4=%s&inputKey=%s&inputValue=%s" % ( self.cfg["ip"], self.cfg['port'], self.cfg['sys_name'], self.cfg['sys_env'], key, val) logcm.print_info("ops link url is : %s " % url) return url
def resize_canvas(img, canvas_width, canvas_height, back_val=255, save_path=None): """ 重新设置画布大小,原始图片居中显示 @param img: 原始图片或路径 @param canvas_width: 底层图片 @param canvas_height: 顶层图片 @param back_val: 背景色值(0-255) @param save_path: 保存路径(可选) @return: 重新设置画布后的图片 """ if canvas_width < 0 or canvas_width < 0: logcm.print_info('Canvas with or height is < 0!', fg='red') # 取得图片 im = get_im(img) # 生成新图片 new_image = empty_img(im.mode, canvas_width, canvas_height, back_val) # 居中粘贴 paste_center(new_image, im) # 保存图片 if save_path is not None: new_image.save(save_path) return new_image
def load(self): """ 取得所有设定值 @return: 设定值的键值字典 """ logcm.print_info("Loading all ops values ...") # 取得访问URL url = self.get_url('load') # OPS服务器的设定画面访问 html = webcm.read_url(url, self.cfg['encoding']) if html is None: return None ops_map = {} soup = htmlcm.to_soup(html) # OPS网页的表格解析 trs = soup.select("body table tr") for i in range(1, len(trs)): tr = trs[i] tds = tr.select("td") if len(tds) == 4: key = tds[2].string val = tds[3].string ops_map[key] = val return ops_map
def deploy_server(self, server_path, war_name, local_path): """ 通过SSH把本地war包发布到远程Tomcat服务器并重启。 @param server_path: 远程Tomcat服务器目录 @param war_name: WAR包名(不含后缀) @param local_path: 本地WAR包所在目录 @return: 无 """ logcm.print_info("Deploy war start : %s" % war_name) # 停止Tomcat,删除已发布War目录及包 cmd_stop = [] cmd_stop.append(server_path + '/bin/shutdown.sh') cmd_stop.append('rm -rf ' + server_path + '/webapps/' + war_name) cmd_stop.append('rm -rf ' + server_path + '/webapps/' + war_name + '.war') self.exe_cmd_list(cmd_stop) # 上传War包 self.upload(local_path, server_path + '/webapps/') # 启动Tomcat cmd_start = [] cmd_start.append(server_path + '/bin/startup.sh') self.exe_cmd_list(cmd_start) logcm.print_info("Deploy war end : %s" % war_name)
def down_img(soup, page_url, img_select, tag_select, local_path, page_no=1): """ 从指定的网页URL,下载所有满足要求的图片到本地 @param soup: 网页Soup对象 @param page_url: 网页URL @param img_select: 图片select语句 @param tag_select: 标签select语句 @param local_path: 本地文件保存路径 @param page_no: 网页页码号 @return:下载到的图片数量 """ src_list = htmlcm.img_src_list(soup, page_url, img_select) logcm.print_info("Page.%d find %d images." % (page_no, len(src_list))) count = 0 for img_src in src_list: # 从链接取得文件名 file_path = urlcm.file_path(img_src) file_name = urlcm.file_name(img_src) logcm.print_info("Page.%d No.%d %s/%s" % (page_no, count + 1, file_path, file_name)) names = htmlcm.tag_name_list(soup, tag_select) if len(names) > 0: local_save_path = local_path + "/" + "_".join(names) else: local_save_path = local_path + "/" + file_path if not filecm.exists(local_save_path, file_name): # 如果本地不存在,保存文件到本地 save_file_url(img_src, page_url, local_save_path, file_name) count = count + 1 return count
def zoom_out(img, ratio=0.9, save_path=None): """ 对原始图片居中缩小指定倍数后画布仍为原始大小 @param img: 原始图片或路径 @param ratio: 缩小比例 @param save_path: 保存路径(可选) @return: 放大后的图片 """ if ratio > 1.0: logcm.print_info('Zoom out ratio must <= 1.0!', fg='red') # 取得图片 im = get_im(img) # 图片原始尺寸 (width, height) = im.size # 缩小图片 new_width = max(int(width * ratio), 2) new_height = max(int(height * ratio), 2) im = resize(im, new_width, new_height) # 重设画布大小 im_zoom = resize_canvas(im, width, height, 0) # 保存图片 if save_path is not None: im_zoom.save(save_path) return im_zoom
def clear(self, name): # 删除Key对应值 logcm.print_info('Clear name: %s' % name) try: self.rds.delete(name) except Exception as e: logcm.print_info("Exception : %s" % e)
def get(self, key, convert=None): """ 取得Redis中指定Key的值 @param key: 指定Key @param convert: 指定转换方法 @return: Key对应值 """ # 取得Key对应值 logcm.print_info('Redis Get by key: %s' % key) try: result = self.rds.get(key) except Exception as e: logcm.print_info("Exception : %s" % e) result = None # 不存在时 if result is None: logcm.print_obj(result, "result", show_header=False) return None # 转换成字符串 result = result.decode() # 无转换时直接返回 if convert is None: logcm.print_obj(result, "result", show_header=False) return result # 执行转换 result = convert(result) logcm.print_obj(result, "result", show_header=False) return result
def load(config_name, default_config, config_path='./config', encoding='utf-8'): """ 从指定路径读取配置,如果配置文件不存在,则使用缺省配置 并保存缺省配置到指定路径。 @param config_name: 配置文件名 @param default_config: 缺省配置 @param config_path: 配置路径 @param encoding: 文字编码 @return: 配置信息 """ if filecm.exists(config_path, config_name): # 文件存在,读取文件 logcm.print_info('配置文件存在:%s/%s' % (config_path, config_name)) cfg_content = filecm.read_str(config_path, config_name, encoding) cfg_info = json.loads(cfg_content) return cfg_info # 文件不存在,返回默认值并初始化配置文件。 logcm.print_info('配置文件不存在:%s/%s' % (config_path, config_name)) # 把对象转成JSON字符串,并格式化 cfg_content = json.dumps(default_config, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) # 保存到json文件 filecm.save_str(cfg_content, encoding, config_path, config_name) return default_config
def tearDown(self): """ 执行了setUp()方法后,不论测试用例执行是否成功,都执行tearDown()方法。 如果tearDown()的代码有异常(除了unittest.SkipTest和AssertionError异常以外),会多算一个error。 :return: """ logcm.print_info('tearDown ...', show_header=False)
def load_sub_items(sheet, sub_items, title_line, start_row, end_row): """ 加载子项目列表 :param sheet:Sheet对象 :param sub_items:子项目定义 @param title_line: 标题行索引 :param start_row:开始行 :param end_row:结束行 :return:子项目数据对象 """ if sheet is None or sub_items is None: return None # 判断数据行是否在合理范围 if start_row < 0 or end_row < start_row or end_row >= sheet.nrows: logcm.print_info("数据行不合理 %d ~ %d" % (start_row, end_row), fg='red') return None # 标题索引Map index_map = find_title_index(sheet, title_line, **sub_items) # 数据开始列 start_col = colname_to_index(sub_items["start_col"]) sub_groups = {} group_key = None for i in range(start_row, end_row + 1): start_val = get_cell_val(sheet, i, start_col) if start_val in sub_items["group_keys"]: group_key = start_val sub_groups[group_key] = [] else: group_data = load_group_data(sheet, sub_items["title_map"], index_map, i) if group_key is not None and group_data is not None: sub_groups[group_key].append(group_data) return sub_groups
def find_title_index(sheet, title_line, **kwargs): """ 在指定Sheet的指定行,查找每个标题对应的列索引 :param sheet:Sheet对象 :param title_line:标题行索引 :param kwargs:其他参数 :return:索引字典 """ index_map = {} for (title, title_key) in kwargs["title_map"].items(): # 查找标题 found_title = False start_col = colname_to_index(kwargs["start_col"]) end_col = colname_to_index(kwargs["end_col"]) for i in range(start_col, end_col + 1): val = get_cell_val(sheet, title_line, i) if title == val: # 记录标题的索引,标记找到 index_map[title] = i found_title = True continue # 如果找不到,则处理停止 if not found_title: logcm.print_info("在第%d行没找到标题:%s" % (title_line + 1, title), fg='red') return None return index_map
def im_convert(src_img, type_from=ImageType.IMG_FILE, type_to=ImageType.IMG_BGR, save_path=None): """ 图片类型转换方法 @:param src_img 原始图片对象或路径 @:param type_from 转换前类型(ImageType) @:param type_to 转换后类型(ImageType) @:param save_path: 保存路径(转换为文件类型时必须) @return: 转换后的图片 """ if src_img is None: logcm.print_info('Src Image is None!', fg='red') return None # 如果类型相同直接返回 if type_from == type_to: return src_img # 先把图片专为成BGR图片 im_bgr = get_bgr_im(src_img, type_from) if im_bgr is None: logcm.print_info('BGR Image Loading Fail!', fg='red') return None # BGR图片 if type_to == ImageType.IMG_BGR: return im_bgr # PIL图片 if type_to == ImageType.IMG_PIL: im_rgb = Image.fromarray(cv2.cvtColor(im_bgr, cv2.COLOR_BGR2RGB)) return im_rgb # SK图片 if type_to == ImageType.IMG_SK: im_rgb = cv2.cvtColor(im_bgr, cv2.COLOR_BGR2RGB) im_sk = img_as_float(im_rgb).astype(np.float64) return im_sk # HSV图片 if type_to == ImageType.IMG_HSV: im_hsv = cv2.cvtColor(src_img, cv2.COLOR_BGR2HSV) return im_hsv # HLS图片 if type_to == ImageType.IMG_HLS: im_hls = cv2.cvtColor(src_img, cv2.COLOR_BGR2HLS) return im_hls # 灰度图片 if type_to == ImageType.IMG_GRAY: im_gray = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY) return im_gray # 图片文件 if type_to == ImageType.IMG_FILE: cv2.imwrite(save_path)
def check_row(sheet, row, title): # 判断标题行是否在合理范围 if row < 0 or row >= sheet.nrows: logcm.print_info("%s的行号不合理! 正常范围:[0~%d), 当前值:%d" % (title, sheet.nrows, row), fg='red') return False return True
def check_col(sheet, col, title): # 判断标题行是否在合理范围 if col < 0 or col >= sheet.ncols: logcm.print_info("%s的列号不合理! 正常范围:[0~%d), 当前值:%d" % (title, sheet.ncols, col), fg='red') return False return True
def getColumns(self, table_name): """ 取得字段列表 :param table_name:表名 :return: 字段列表 """ logcm.print_info("Get columns of table: %s " % table_name) logcm.print_obj(table_name, "table_name")
def __del__(self): """ 关闭连接 @return: 无 """ if self.server is not None: logcm.print_info("Close jenkins server.")