Exemplo n.º 1
0
    def create_pbtxt(cls, save_path: str, mapping: dict) -> bool:
        """
        创建labelmap.pbtxt文件

        @param {str} save_path - 文件保存路径
        @param {dict} mapping - mapping.josn文件对应的字典

        @return {bool} - 处理结果
        """
        try:
            _fix_str = "item {\n    id: %d\n    name: '%s'\n}"
            _list = []

            # 按int进行排序
            _class_int = sorted(mapping['class_int'].items(),
                                key=lambda kv: (kv[1], kv[0]))
            for _item in _class_int:
                _id = _item[1]
                _name = mapping['class'][_item[0]]
                _list.append(_fix_str % (_id, _name))

            # 保存到文件中
            FileTool.create_dir(save_path, exist_ok=True)
            with open(os.path.join(save_path, 'labelmap.pbtxt'), 'wb') as f:
                f.write(str.encode('\n\n'.join(_list), encoding='utf-8'))

            return True
        except:
            print('create_pbtxt error: \r\n%s' % (traceback.format_exc(), ))
            return False
Exemplo n.º 2
0
    def create_cc_type_pbtxt(cls, save_path: str) -> bool:
        """
        创建CC的款式labelmap.pbtxt文件

        @param {str} save_path - 文件保存路径

        @return {bool} - 处理结果
        """
        try:
            _fix_str = "item {\n    id: %d\n    name: '%s'\n}"
            _list = []
            # 只处理所设定的分类
            for _type in USE_CLASS_TEXT_LIST:
                _id = CLASS_TEXT_TO_INT[_type]
                _name = ''
                if _type in PROP_TYPE_TRAN_DICT.keys():
                    _name = PROP_TYPE_TRAN_DICT[_type]
                else:
                    for _dict_key in PROP_VALUE_TRAN_DICT.keys():
                        if _type in PROP_VALUE_TRAN_DICT[_dict_key].keys():
                            _name = PROP_VALUE_TRAN_DICT[_dict_key][_type]
                            break

                _list.append(_fix_str % (_id, _name))

            # 保存到文件中
            FileTool.create_dir(save_path, exist_ok=True)
            with open(os.path.join(save_path, 'labelmap.pbtxt'), 'wb') as f:
                f.write(str.encode('\n\n'.join(_list), encoding='utf-8'))

            return True
        except:
            print('create_cc_type_pbtxt error: \r\n%s' %
                  (traceback.format_exc(), ))
            return False
Exemplo n.º 3
0
    def __init__(self, desired_caps: dict, **kwargs):
        """
        构造函数

        @param {dict} desired_caps=None - 设备连接参数字典
            deviceName {str} - 使用的手机或模拟器类型, 填入 adb devices -l 显示的设备连接名,
                示例: 127.0.0.1:21513
            appPackage {str} - 连接时运行的 Android 应用的包名, 例如: com.android.browser
            appActivity {str} - 包中所要启动的 Android acticity, 例如: .BrowserActivity
        @param {kwargs} - 扩展参数,支持的扩展参数包括
            shell_encoding {str} - shell命令的编码方式, 在使用到命令行工具时使用, 默认为 'utf-8'
            adb_name {str} - adb命令的启动名称, 安卓adb版专用, 默认为 'adb'
            tmp_path {str} - 临时目录, 处理adb资源文件, 安卓adb版专用, 默认为当前工作目录
        """
        self._desired_caps = {}
        self._desired_caps.update(desired_caps)

        # 其他参数
        self.shell_encoding = kwargs.get('shell_encoding', 'utf-8')
        self.adb_name = kwargs.get('adb_name', 'adb')
        self.tmp_path = os.path.abspath(kwargs.get('tmp_path', ''))
        FileTool.create_dir(self.tmp_path, exist_ok=True)

        # 判断命令行
        self.grep_str = 'findstr' if sys.platform == 'win32' else 'grep'

        # 缓存字典
        self.cache = dict()

        # 要安装到设备上的文件路径
        self._file_path = os.path.join(
            os.path.realpath(os.path.dirname(__file__)), 'adb_apk')
Exemplo n.º 4
0
    def clean_file_path(cls, path: str):
        """
        清理文件目录
        1、批量删除带括号的图片文件(重复下载)
        2、删除宣传图片
        2、将文件名修改为"产品编号_main/detail_序号"的格式
        3、将文件夹按款式进行分类

        @param {str} path - 要清理的文件夹

        @return {iter_list} - 通过yield返回的处理进度信息清单
            [总文件数int, 当前已处理文件数int, 是否成功]
        """
        try:
            _path = path
            if not (_path.endswith('/') or _path.endswith('\\')):
                _path = _path + '/'

            # 创建分类目录
            _class_path = os.path.join(FileTool.get_parent_dir(_path),
                                       FileTool.get_dir_name(_path) + '_class')
            if not os.path.exists(_class_path):
                FileTool.create_dir(_class_path, exist_ok=True)

            # 获取目录清单
            _dir_list = cls._get_child_dir_list(path, with_root=True)
            _total = len(_dir_list)
            _deal_num = 0

            # 先返回进度情况
            if _total == 0:
                yield [_deal_num, _total, True]
                return

            # 遍历目录执行处理
            for _dir in _dir_list:
                yield [_deal_num, _total, True]
                cls._clean_file_path(_dir, _class_path)
                _deal_num += 1

            yield [_deal_num, _total, True]
        except:
            print('clean_file_path error: %s\r\n%s' %
                  (path, traceback.format_exc()))
            yield [-1, -1, False]
Exemplo n.º 5
0
    def install(self, dir_name: str):
        """
        安装插件

        @param {str} dir_name - 插件所在路径名
        """
        _plugin_path = os.path.join(self.plugin_path, dir_name)

        # 获取插件配置
        _plugin_config = SimpleXml(os.path.join(
            _plugin_path, 'plugin.xml')).to_dict()['config']['info']

        # 复制文件到对应目录
        _static_path = os.path.join(_plugin_path,
                                    'static')  # 静态文件, js/css/img/html等
        if os.path.exists(_static_path):
            # 先创建目标文件
            _dest_path = os.path.join(self.static_path, 'plugin',
                                      _plugin_config['plugin_name'])
            FileTool.create_dir(_dest_path, exist_ok=True)
            FileTool.copy_all_with_path(src_path=_static_path,
                                        dest_path=_dest_path,
                                        exist_ok=True)

        _templates_path = os.path.join(_plugin_path, 'templates')  # 模板文件
        if os.path.exists(_templates_path):
            # 先创建目标文件
            _dest_path = os.path.join(self.templates_path, 'plugin',
                                      _plugin_config['plugin_name'])
            FileTool.create_dir(_dest_path, exist_ok=True)
            FileTool.copy_all_with_path(src_path=_templates_path,
                                        dest_path=_dest_path,
                                        exist_ok=True)

        _config_path = os.path.join(_plugin_path, 'config')  # 配置文件
        if os.path.exists(_config_path):
            # 先创建目标文件
            _dest_path = os.path.join(self.config_path, 'plugin',
                                      _plugin_config['plugin_name'])
            FileTool.create_dir(_dest_path, exist_ok=True)
            FileTool.copy_all_with_path(src_path=_config_path,
                                        dest_path=_dest_path,
                                        exist_ok=True)

        # 登记安装信息
        self._exec_sql("replace into t_installed values(?, ?, ?)",
                       para=(_plugin_config['plugin_name'],
                             _plugin_config['show_name'], dir_name))

        # 加载插件
        self.load_plugin(_plugin_config['plugin_name'])
Exemplo n.º 6
0
    def UploadFile(cls,
                   upload_type: str,
                   note: str,
                   interface_seq_id: str,
                   methods=['POST']):
        """
        上传文件(单文件上传)  (/api/Qa/UploadFiles/<upload_type>/<note>/<interface_seq_id>)

        @param {str} upload_type - 文件类型,必须在UploadFileConfig表中有配置
        @param {str} note - 文件注解
        @param {str} interface_seq_id - 客户端序号,客户端可传入该值来支持异步调用

        @return {str} - 返回回答的json字符串
            status : 处理状态
                00000 - 成功, 返回一条回答
                10001 - 没有指定上传文件
                2XXXX - 处理失败
            msg : 处理状态对应的描述
            answer_type: 'text'或'json',指示返回的答案是文本数组,还是一个json对象
            answers : 回答内容
            url : 文件上传后的url,含文件名和url路径
        """
        _ret_json = {
            'interface_seq_id': interface_seq_id,
            'status': '00000',
            'msg': 'success',
            'answer_type': 'text',
            'answers': [],
            'url': ''
        }
        _qa_loader = RunTool.get_global_var('QA_LOADER')
        try:
            if 'file' not in request.files or request.files[
                    'file'].filename == '':
                _ret_json['status'] = '10001'
                _ret_json['msg'] = 'No file upload!'
                return jsonify(_ret_json)

            # 获取上传类型配置
            _upload_config = UploadFileConfig.get_or_none(
                UploadFileConfig.upload_type == upload_type)
            if _upload_config is None:
                _ret_json['status'] = '10002'
                _ret_json['msg'] = 'upload type not exists!'
                return jsonify(_ret_json)

            # 检查文件大小
            if _upload_config.size > 0:
                if request.content_length > _upload_config.size * 1024 * 1024:
                    _ret_json['status'] = '10003'
                    _ret_json['msg'] = 'upload file size to large!'
                    return jsonify(_ret_json)

            # 检查文件类型是否支持
            _file = request.files['file']
            _old_filename = _file.filename
            _file_ext = FileTool.get_file_ext(_old_filename)
            _allow_ext = eval(_upload_config.exts.upper())
            if len(_allow_ext) > 0 and _file_ext.upper() not in _allow_ext:
                _ret_json['status'] = '10004'
                _ret_json['msg'] = 'Type [%s] not allow upload [.%s] file!' % (
                    upload_type, _file_ext)
                return jsonify(_ret_json)

            # 处理新的文件名
            def _replace_var_fun(m):
                _match_str = m.group(0)
                _value = None
                if _match_str.startswith('{$datetime='):
                    # 按格式化字符替换当前的时间
                    _key = _match_str[11:-2]
                    _value = datetime.datetime.now().strftime(_key)
                elif _match_str.startswith('{$uuid='):
                    # 指定uuid字符类型
                    _key = _match_str[7:-2]
                    str(uuid.uuid1())
                    _value = eval('str(uuid.uuid%s())' % _key)
                elif _match_str.startswith('{$random'):
                    # 产生指定两个整数之间的随机数,总位数与最大的数一致,左补零
                    _key = _match_str[8:-2]
                    _args = eval('(%s)' % _key)
                    _value = StringTool.fill_fix_string(
                        str(random.randint(*_args)), len(_args[1]), '0')
                elif _match_str.startswith('{$file_ext='):
                    # 原文件扩展名
                    _value = _file_ext
                elif _match_str.startswith('{$file_name='):
                    # 原文件指定位置的字符
                    _key = _match_str[12:-2]
                    _args = eval('(%s)' % _key)
                    if len(_args) > 1:
                        _value = _old_filename[_args[0]:_args[1]]
                    else:
                        _value = _old_filename[_args[0]:]

                if _value is not None:
                    return str(_value)
                else:
                    return _match_str

            if _upload_config.rename == '':
                _new_filename = _old_filename
            else:
                _new_filename = re.sub(r'\{\$.+?\$\}', _replace_var_fun,
                                       _upload_config.rename, re.M)

            # 处理保存文件路径和url路径
            if _upload_config.url != '':
                _ret_json['url'] = '%s/%s' % (_upload_config.url,
                                              _new_filename)

            _save_path = os.path.realpath(
                os.path.join(_qa_loader.execute_path, _upload_config.save_path,
                             _new_filename))

            # 创建文件目录
            FileTool.create_dir(os.path.split(_save_path)[0], exist_ok=True)

            # 保存文件
            _file.save(_save_path)

            # 上传后处理
            _after = eval(_upload_config.after)
            if len(_after) > 0:
                _after_fun = _qa_loader.plugins['upload_after'][_after[0]][
                    _after[1]]
                _status, _msg, _answers = _after_fun(upload_type, note,
                                                     _new_filename, _save_path,
                                                     _ret_json['url'],
                                                     **_after[2])
                _ret_json['status'] = _status
                _ret_json['msg'] = _msg
                if len(_answers) > 0 and type(_answers[0]) == dict:
                    _ret_json['answer_type'] = 'json'
                    _ret_json['answers'] = _answers[0]
                else:
                    _ret_json['answers'] = _answers
                if _ret_json['status'] != '00000':
                    # 后处理失败,删除文件
                    FileTool.remove_file(_save_path)
                    if _qa_loader.logger:
                        _qa_loader.logger.debug(
                            'remove upload file [dest:%s][source:%s] when after deal error[%s]: %s'
                            % (_new_filename, _old_filename, _status, _msg))
        except:
            if _qa_loader.logger:
                _qa_loader.logger.error('Exception: %s' %
                                        traceback.format_exc(),
                                        extra={'callFunLevel': 1})
            _ret_json = {
                'interface_seq_id': interface_seq_id,
                'status': '20001',
                'msg': '上传文件异常'
            }

        return jsonify(_ret_json)
Exemplo n.º 7
0
    def labelimg_crop_pic_by_flags(cls,
                                   path: str,
                                   dest_path: str,
                                   copy_no_flag_pic: bool = True,
                                   with_sub_dir: bool = True,
                                   fix_len: int = 10):
        """
        根据标注进行图片截图处理

        @param {str} path - 需要处理的目录
        @param {str} dest_path - 截图图片存放目录
        @param {bool} copy_no_flag_pic=True - 直接复制没有标注的图片
        @param {bool} with_sub_dir=True - 是否按原目录结构存储图片
        @param {int} fix_len=10 - 图片重命名的文件名长度

        @returns {iter_list} - 通过yield返回的处理进度信息清单
            [总文件数int, 当前已处理文件数int, 是否成功]
        """
        try:
            # 获取所有要处理的图片清单
            _file_list = cls._get_pic_file_list(path)
            _total = len(_file_list)
            _deal_num = 0

            # 先返回进度情况
            if _total == 0:
                yield [_deal_num, _total, True]
                return

            # 创建复制文件夹
            FileTool.create_dir(dest_path, exist_ok=True)

            # 遍历处理
            _rename_index = 1
            _src_path = os.path.realpath(path)
            _dest_path = os.path.realpath(dest_path)
            for _file in _file_list:
                # 当前进展
                yield [_deal_num, _total, True]

                # 路径处理
                _file_path, _filename = os.path.split(_file)
                if with_sub_dir:
                    # 创建子目录
                    _dest_path = os.path.join(
                        os.path.realpath(dest_path),
                        os.path.realpath(_file_path)[len(_src_path):].strip(
                            '/\\'))
                    FileTool.create_dir(_dest_path, exist_ok=True)

                # 获取标注文件
                _ext = FileTool.get_file_ext(_filename)
                _xml_file = os.path.join(_file_path,
                                         _filename[0:-len(_ext)] + 'xml')

                if not os.path.exists(_xml_file):
                    # 标注文件不存在
                    if copy_no_flag_pic:
                        # 直接复制文件
                        shutil.copy(
                            _file,
                            os.path.join(
                                _dest_path,
                                StringTool.fill_fix_string(
                                    str(_rename_index), fix_len, '0') + '.' +
                                _ext))
                        _rename_index += 1

                    # 下一个
                    _deal_num += 1
                    continue

                # 将图片放入内存
                with open(_file, 'rb') as _fid:
                    _file_bytes = _fid.read()
                    _image = Image.open(BytesIO(_file_bytes))

                # 处理标注
                _tree = ET.parse(_xml_file)
                _root = _tree.getroot()

                for _member in _root.findall('object'):
                    # 逐个标注进行处理
                    _crop_image = _image.crop(
                        (int(_member[4][0].text), int(_member[4][1].text),
                         int(_member[4][2].text), int(_member[4][3].text)))

                    _crop_image.save(os.path.join(
                        _dest_path,
                        StringTool.fill_fix_string(str(_rename_index), fix_len,
                                                   '0') + '.' + _ext),
                                     format='JPEG')

                    _rename_index += 1

                # 下一个
                _deal_num += 1

            # 返回结果
            yield [_total, _total, True]
        except:
            print('labelimg_crop_pic_by_flags error: %s\r\n%s' %
                  (path, traceback.format_exc()))
            yield [-1, -1, False]
Exemplo n.º 8
0
    def labelimg_copy_flags_pics(cls,
                                 input_path: str,
                                 output_path: str,
                                 use_mapping: bool = False,
                                 mapping: dict = None):
        """
        按类别复制图片和标注文件到指定目录

        @param {str} input_path - 图片路径
        @param {str} output_path - 输出路径
        @param {bool} use_mapping=False - 是否使用mapping处理映射
        @param {dict} mapping=None - mapping.josn字典

        @returns {iter_list} - 通过yield返回的处理进度信息清单
            [总文件数int, 当前已处理文件数int, 是否成功]
        """
        try:
            # 遍历所有文件夹,获取需要处理的文件数量
            _file_list = cls._get_labelimg_annotation_file_list(input_path)
            _total = len(_file_list)
            _deal_num = 0

            # 先返回进度情况
            if _total == 0:
                yield [_deal_num, _total, True]
                return

            # 创建复制文件夹
            FileTool.create_dir(output_path, exist_ok=True)

            # 遍历处理
            for _file in _file_list:
                # 当前进展
                yield [_deal_num, _total, True]

                # 逐个标注文件进行处理
                _tree = ET.parse(_file)
                _root = _tree.getroot()

                _annotations = dict()
                _annotations['filename'] = _root.find('filename').text
                _annotations['file_path'] = os.path.join(
                    os.path.split(_file)[0], _annotations['filename'])

                # 获取信息字典
                _info_dict = ExtendLib.get_info_dict(_annotations['file_path'],
                                                     mapping['info_key_dict'])

                # 逐个标签处理
                _save_class_path = ''  # 要保存到的分类路径
                _is_copy = False  # 标注是否已复制文件
                _new_xml_name = ''  # 新的xml名
                for _member in _root.findall('object'):
                    _member_class = _member[0].text
                    if use_mapping:
                        # 使用映射处理
                        if _member_class == mapping['set_by_info'][
                                'class_name']:
                            # 需要获取真实的信息
                            if mapping['set_by_info'][
                                    'info_tag'] in _info_dict.keys():
                                _member_class = _info_dict[
                                    mapping['set_by_info']['info_tag']]

                                # 变更分类名
                                _member[0].text = _member_class

                        # 过滤不需要的类别
                        if _member_class not in mapping['class_int'].keys():
                            _deal_num += 1
                            continue

                        # 保存分类路径
                        _save_class_path = os.path.join(
                            output_path, mapping['class'][_member_class])
                    else:
                        # 普通分类
                        _save_class_path = os.path.join(
                            output_path, _member_class)

                    # 复制文件
                    if not _is_copy:
                        # 处理重复文件名
                        _file_name = FileTool.get_file_name_no_ext(
                            _annotations['filename'])
                        _file_ext = FileTool.get_file_ext(
                            _annotations['filename'])
                        _rename_num = 1
                        _new_file_name = '%s.%s' % (_file_name, _file_ext)
                        _new_xml_name = '%s.xml' % (_file_name, )
                        while os.path.exists(
                                os.path.join(_save_class_path,
                                             _new_file_name)):
                            _new_file_name = '%s_%d.%s' % (
                                _file_name, _rename_num, _file_ext)
                            _new_xml_name = '%s_%d.xml' % (_file_name,
                                                           _rename_num)
                            _rename_num += 1

                        # 创建文件夹
                        FileTool.create_dir(_save_class_path, exist_ok=True)
                        shutil.copyfile(
                            _annotations['file_path'],
                            os.path.join(_save_class_path, _new_file_name))

                        # 修改xml里面的文件名和文件路径
                        _root.find('filename').text = _new_file_name
                        _root.find('path').text = os.path.join(
                            _save_class_path, _new_file_name)

                        _is_copy = True

                if _is_copy:
                    # 有修改xml内容
                    _tree.write(os.path.join(_save_class_path, _new_xml_name),
                                encoding='utf-8',
                                method="xml",
                                xml_declaration=None)

                # 继续循环处理
                _deal_num += 1

            # 返回结果
            yield [_total, _total, True]
        except:
            print('labelimg_copy_flags_pics error: %s\r\n%s' %
                  (input_path, traceback.format_exc()))
            yield [-1, -1, False]
Exemplo n.º 9
0
    def labelimg_to_tfrecord(cls,
                             input_path: str,
                             output_file: str,
                             num_per_file: int = None,
                             class_to_int_fun=None,
                             use_mapping: bool = False,
                             mapping: dict = None,
                             copy_img_path=None):
        """
        将LabelImg标注后的图片目录转换为TFRecord格式文件

        @param {str} input_path - 输入图片清单目录,注意labelimg标注的xml与图片文件在一个目录中
        @param {str} output_file - 输出的TFRecord格式文件路径(含文件名,例如xx.record)
        @param {int} num_per_file=None - 拆分每个TFRecord文件的文件大小
        @param {function} class_to_int_fun=None - 将分类名转换为int的函数
            如果传None代表类名为数字,可以直接将类名转换为数字
        @param {bool} use_mapping=False - 是否使用mapping.json数据处理转换
        @param {dict} mapping=None - mapping.json字典
        @param {str} copy_img_path=None - 如果传值了则复制对应的图片到对应目录

        @returns {iter_list} - 通过yield返回的处理进度信息清单
            [总文件数int, 当前已处理文件数int, 是否成功]
        """
        try:
            # 遍历所有文件夹,获取需要处理的文件数量
            _file_list = cls._get_labelimg_annotation_file_list(input_path)
            _total = len(_file_list)
            _deal_num = 0

            # 先返回进度情况
            if _total == 0:
                yield [_deal_num, _total, True, {}]
                return

            # 基础变量
            _output_file = output_file
            _current_package = 1  # 当前包序号
            _package_file_num = 0  # 当前包文件数量
            _total_pkg_num = 1  # 包总数量
            if num_per_file is not None:
                _total_pkg_num = math.ceil(_total / num_per_file)
                _output_file = '%s-%.5d-of-%.5d' % (
                    output_file, _current_package, _total_pkg_num)

            # 创建文件夹
            FileTool.create_dir(os.path.split(_output_file)[0], exist_ok=True)

            if copy_img_path is not None:
                FileTool.create_dir(copy_img_path, exist_ok=True)

            # 标签的统计信息
            _flags_count = dict()

            # TFRecordWriter
            _writer = tf.io.TFRecordWriter(_output_file)

            # 遍历文件进行处理
            _writer_closed = False
            for _file in _file_list:
                # 当前进展
                yield [_deal_num, _total, True, _flags_count]

                # 写入当前文件
                _tf_example = cls._create_labelimg_tf_example(
                    _file,
                    class_to_int_fun=class_to_int_fun,
                    use_mapping=use_mapping,
                    mapping=mapping,
                    copy_img_path=copy_img_path,
                    flags_count=_flags_count)
                _deal_num += 1

                if _tf_example is not None:
                    _writer.write(_tf_example.SerializeToString())
                    _package_file_num += 1
                else:
                    # 没有找到写入信息,直接下一个
                    continue

                if num_per_file is not None:
                    if _package_file_num >= num_per_file:
                        # 一个文件数据已写够
                        _writer.close()
                        if _current_package >= _total_pkg_num:
                            # 已经是最后一个包
                            _writer_closed = True
                            break
                        else:
                            # 要处理下一个包
                            _current_package += 1
                            _package_file_num = 0
                            _output_file = '%s-%.5d-of-%.5d' % (
                                output_file, _current_package, _total_pkg_num)
                            _writer = tf.io.TFRecordWriter(_output_file)

            # 最后的保存
            if not _writer_closed:
                _writer.close()

            # 判断是否要修改文件名(实际数量少于预期数量)
            if _current_package < _total_pkg_num:
                for _index in range(1, _current_package + 1):
                    os.rename(
                        '%s-%.5d-of-%.5d' %
                        (output_file, _index, _total_pkg_num),
                        '%s-%.5d-of-%.5d' %
                        (output_file, _index, _current_package),
                    )

            # 返回结果
            yield [_total, _total, True, _flags_count]
        except:
            print('labelimg_to_tfrecord error: %s\r\n%s' %
                  (input_path, traceback.format_exc()))
            yield [-1, -1, False, {}]
Exemplo n.º 10
0
    def __init__(self,
                 file: str,
                 is_resume: bool = True,
                 file_size: int = None,
                 md5: str = None,
                 is_overwrite: bool = False,
                 temp_ext: str = 'tmp',
                 info_ext: str = 'info',
                 extend_info: dict = None,
                 thread_num: int = 1,
                 block_size: int = 4096,
                 cache_size: int = 1024,
                 auto_expand: bool = True):
        """
        初始化文件保存对象

        @param {str} file - 文件保存路径(含文件名)
        @param {bool} is_resume=True - 指定是否续传(自动查找已下载的信息), 如果不指定续传将自动删除原来已下载临时文件
            注:如果指定续传,且可以找到原来的临时文件,则以下参数将使用原来的信息,如果有传入则会进行差异值的校验:
                file_size、md5
        @param {int} file_size=None - 文件大小,单位为byte, 如果为None代表未知文件大小, 此时auto_expand参数固定为True
        @param {str} md5=None - 验证文件的md5字符串,如果不传代表不进行验证
        @param {bool} is_overwrite=False - 是否覆盖已有文件,如果为否,则目标文件已存在的情况下抛出异常
        @param {str} temp_ext='tmp' - 处理过程中临时文件扩展名
        @param {str} info_ext='info' - 处理过程中信息文件扩展名
        @param {dict} extend_info=None - 处理过程中要保存的信息字典,例如保存文件下载路径,引用页等信息
        @param {int} thread_num=1 - 写入处理线程数量
        @param {int} block_size=4096 - 每次写入块大小,单位为byte
        @param {int} cache_size=1024 - 单线程缓存大小,单位为kb(注意:真实缓存大小还需要乘以处理线程数量)
        @param {bool} auto_expand=True - 是否自动扩展文件大小(否则在初始化时会自动创建指定大小的文件)

        @throws {FileExistsError} - 如果下载文件已存在且不允许覆盖的情况抛出异常
        @throws {FileNotFoundError} - 续传情况下临时文件不存在则抛出异常
        @throws {InfoFileLockError} - 如果已打开信息文件进行文件存储处理,抛出该异常
        """
        # 检查文件是否存在
        self._file = os.path.abspath(file)
        self._path, self._filename = os.path.split(self._file)
        if os.path.exists(self._file):
            # 文件已存在
            if is_overwrite:
                FileTool.remove_file(self._file)
            else:
                raise FileExistsError('file exists: %s' % self._file)
        else:
            # 创建目录
            FileTool.create_dir(self._path, exist_ok=True)

        # 文件信息字典,该字典登记文件基本信息和写入情况
        self._info: dict = None

        # 锁文件,控制一个文件不能被多个类处理, 先尝试创建锁文件,如果创建失败会抛出异常
        self._lock_file = os.path.join(self._path,
                                       '%s.%s' % (self._filename, 'lock'))
        try:
            self._lock_file_handle = os.open(
                self._lock_file, os.O_CREAT | os.O_EXCL | os.O_RDWR)
        except:
            raise InfoFileLockError('info file is locked')

        try:
            # 获取是否debug状态
            self._debug_on = DebugTool.is_debug_on()
            self._lock_print_timeout = None
            if self._debug_on:
                self._lock_print_timeout = 5.0  # 打印锁等待超时时间

            # 处理信息字典、临时文件、信息文件
            self._temp_file = os.path.join(
                self._path, '%s.%s' % (self._filename, temp_ext))
            self._info_file = os.path.join(
                self._path, '%s.%s' % (self._filename, info_ext))
            self._auto_expand = auto_expand
            self._thread_num = thread_num
            self._block_size = block_size
            self._cache_size = cache_size * 1024

            # 数据处理锁
            self._cache_info_lock = threading.RLock()  # 缓存信息更新锁
            self._tmp_file_lock = threading.RLock()  # 缓存文件写入锁
            self._is_finished = False  # 要控制的完成状态
            self._dealed_finished_lock = threading.RLock()  # 控制多线程操作结束函数的状态更新锁
            self._dealed_finished = False  # 控制多线程操作结束函数只执行一次的变量

            if is_resume and os.path.exists(self._info_file):
                # 自动续传情况
                self._info_file_handle = open(self._info_file,
                                              'r+',
                                              encoding='utf-8')
                self._info_file_handle.seek(0)
                self._info = json.loads(self._info_file_handle.read())

                # 检查传入信息是否一致
                if file_size is not None and file_size != self._info[
                        'file_size']:
                    raise AttributeError(
                        'resume info [file_size] inconsistency, info file [%s], now [%s]'
                        % (str(self._info['file_size']), str(file_size)))

                if md5 is not None and md5 != self._info['md5']:
                    raise AttributeError(
                        'resume info [md5] inconsistency, info file [%s], now [%s]'
                        % (self._info['md5'], md5))

                # 检查临时文件
                self._temp_file = os.path.join(self._path,
                                               self._info['tmp_file'])
                if not os.path.exists(self._temp_file):
                    # 临时文件不存在
                    raise FileNotFoundError('temp file is not found: %s' %
                                            self._temp_file)

                self._tmp_file_handle = open(self._temp_file, 'rb+')
                self._tmp_file_handle.seek(0)
            else:
                # 删除已存在的临时文件信息
                if os.path.exists(self._temp_file):
                    FileTool.remove_file(self._temp_file)

                if os.path.exists(self._info_file):
                    FileTool.remove_file(self._info_file)

                # 形成信息字典
                self._info = {
                    'tmp_file': '%s.%s' % (self._filename, temp_ext),  # 临时文件名称
                    'file_size':
                    -1 if file_size is None else file_size,  # 文件大小
                    'write_size': 0,  # 已写入数据大小
                    'md5': '' if md5 is None else md5,  # md5校验值
                    'extend_info':
                    {} if extend_info is None else extend_info,  # 传入的扩展信息
                    # 存储索引,按位置顺序在数组中登记未写入区间,数组每一项登记未写入数据的开始位置和结束位置
                    'store_index': [[0, file_size - 1]]
                }

                # 生成临时文件
                self._tmp_file_handle = open(self._temp_file, 'wb')
                if not auto_expand and file_size is not None:
                    # 直接生成指定大小的文件
                    self._tmp_file_handle.seek(file_size - 1)  # 跳到指定位置
                    self._tmp_file_handle.write(b'\x00')  # 一定要写入一个字符,否则无效
                    self._tmp_file_handle.flush()

                # 写入信息字典文件
                self._info_file_handle = open(self._info_file,
                                              'w',
                                              encoding='utf-8')
                self._write_info_file()

            # 合并存储索引,把碎片合并成为大块
            self._info['store_index'] = self._f_merge_store_index(
                self._info['store_index'])

            # 初始化缓存等信息
            if self._info['file_size'] == -1:
                # 如果没有文件大小的情况,不支持拆分多写入线程和一次性创建指定大小文件的情况
                self._thread_num = 1
                self._auto_expand = True

            # 缓存处理
            self._max_cache_pos = [
                -1,
            ]  # 当前缓存分配到的区域最大位置
            self._cache = dict()
            for _i in range(self._thread_num):
                self._cache[_i] = {
                    'start': -1,  # 缓存数据对应文件的写入位置, -1代表没有设置
                    'size': 0,  # 缓存数据大小
                    'buffer': bytes(),  # 具体的缓存数据
                    'end_pos': -1,  # 该缓存对应线程要处理的文件块结束位置
                    'lock': threading.RLock(),  # 用于缓存线程处理的锁)
                    'get_start': -1,  # 当前正在获取的数据的开始位置
                    'get_size': 0,  # 当前要获取数据的大小
                }

            # 分配每个缓存要处理文件区域
            for _i in range(self._thread_num):
                self._set_cache_area(_i)
        except:
            # 如果初始化出现异常,清理文件句柄及锁文件
            self._clear_file_handle_and_lock()
            raise
Exemplo n.º 11
0
    def setUpClass(cls):
        print("test class start =======>")
        print("初始化日志类")
        try:
            # 删除临时日志
            FileTool.remove_files(path=_log_path, regex_str='.*\\.log')
        except:
            pass

        _logger_conf = os.path.realpath(
            os.path.join(_temp_path, os.path.pardir, os.path.pardir,
                         'file_transfer/test_file_transfer.json'))
        cls.logger = simple_log.Logger(
            conf_file_name=_logger_conf,
            logger_name=simple_log.EnumLoggerName.File,
            config_type=simple_log.EnumLoggerConfigType.JSON_FILE,
            # logfile_path=_TEMP_DIR + '/log/test_case_client.log',
            is_create_logfile_by_day=True,
        )
        cls.logger.setLevelWithHandler(simple_log.DEBUG)

        print('初始化并启动服务,简单服务,无SSL,无服务发现')
        cls.server_no_ssl_no_zoo_opts = SimpleGRpcServer.generate_server_opts(
            ip='127.0.0.1',
            port=50051,
            max_workers=50,
            max_connect=400,
            is_health_check=True)
        cls.server_no_ssl_no_zoo = SimpleGRpcServer(
            server_name='ServerNoSslNoZoo',
            logger=cls.logger,
            log_level=simple_log.INFO)

        _push_services = GRpcPushServicerGenerater(work_dir=_temp_path,
                                                   lock_in_work_dir=True,
                                                   logger=cls.logger,
                                                   is_use_global_logger=False)
        _pull_services = GRpcPullServicerGenerater(work_dir=_temp_path,
                                                   lock_in_work_dir=True,
                                                   logger=cls.logger,
                                                   is_use_global_logger=False)

        cls.server_no_ssl_no_zoo.start_server(
            server_opts=cls.server_no_ssl_no_zoo_opts,
            servicer_list={
                'servicer_file_transfer_push': _push_services.get_servicer(),
                'servicer_file_transfer_pull': _pull_services.get_servicer()
            },
            is_wait=True)

        # 先创建本地随机文件
        print('创建本地随机文件')
        FileTool.create_dir(_temp_path, exist_ok=True)
        if not os.path.exists(_temp_file):
            with open(_temp_file, 'wb') as _file:
                _real_size = 561 * 1024  # 561kb
                _file.truncate(_real_size)  # 对于已存在的文件,有可能比较大,要进行截断处理
                _file.seek(_real_size - 1)  # 跳到指定位置
                _file.write(b'\x00')  # 一定要写入一个字符,否则无效
                _file.seek(random.randint(0, _real_size - 1 - 1024))
                _file.write(bytes('abcdefg', encoding='utf-8'))
                _file.flush()
Exemplo n.º 12
0
    def labelimg_copy_flags_pics(cls,
                                 input_path: str,
                                 output_path: str,
                                 is_cc: bool = False):
        """
        按类别复制图片和标注文件到指定目录

        @param {str} input_path - 图片路径
        @param {str} output_path - 输出路径
        @param {bool} is_cc=False - 是否CC项目

        @returns {iter_list} - 通过yield返回的处理进度信息清单
            [总文件数int, 当前已处理文件数int, 是否成功]
        """
        try:
            # 遍历所有文件夹,获取需要处理的文件数量
            _file_list = cls._get_labelimg_annotation_file_list(input_path)
            _total = len(_file_list)
            _deal_num = 0

            # 先返回进度情况
            if _total == 0:
                yield [_deal_num, _total, True]
                return

            # 创建复制文件夹
            FileTool.create_dir(output_path, exist_ok=True)

            # 遍历处理
            for _file in _file_list:
                # 当前进展
                yield [_deal_num, _total, True]

                # 逐个标注文件进行处理
                _tree = ET.parse(_file)
                _root = _tree.getroot()

                _annotations = dict()
                _annotations['filename'] = _root.find('filename').text
                _annotations['file_path'] = os.path.join(
                    os.path.split(_file)[0], _annotations['filename'])

                # 逐个标签处理
                _save_class_path = ''  # 要保存到的分类路径
                _is_copy = False  # 标注是否已复制文件
                _is_change_class = False  # 标注是否有修改分类名
                _new_xml_name = ''  # 新的xml名
                for _member in _root.findall('object'):
                    _member_class = _member[0].text
                    if is_cc:
                        # CC专属的类型转换
                        if _member_class == '翡翠':
                            # 需要获取真实的信息
                            _info_file = os.path.join(
                                os.path.split(_file)[0], 'info.json')
                            _info = dict()
                            with open(_info_file, 'rb') as f:
                                _eval = str(f.read(), encoding='utf-8')
                                _info = eval(_eval)

                            if _info['款式'] == '挂件':
                                # 挂件,需要二级分类
                                _member_class = _info['挂件类型']
                            else:
                                # 一级分类
                                _member_class = _info['款式']

                            # 变更分类名
                            _member[0].text = _member_class
                            _is_change_class = True

                        # 过滤不需要的类别
                        if _member_class not in USE_CLASS_TEXT_LIST:
                            _deal_num += 1
                            continue

                        # 保存分类路径
                        _save_class_path = os.path.join(
                            output_path, cls._cc_get_class_text(_member_class))
                    else:
                        # 普通分类
                        _save_class_path = os.path.join(
                            output_path, _member_class)

                    # 复制文件
                    if not _is_copy:
                        # 处理重复文件名
                        _file_name = FileTool.get_file_name_no_ext(
                            _annotations['filename'])
                        _file_ext = FileTool.get_file_ext(
                            _annotations['filename'])
                        _rename_num = 1
                        _new_file_name = '%s.%s' % (_file_name, _file_ext)
                        _new_xml_name = '%s.xml' % (_file_name, )
                        while os.path.exists(
                                os.path.join(_save_class_path,
                                             _new_file_name)):
                            _new_file_name = '%s_%d.%s' % (
                                _file_name, _rename_num, _file_ext)
                            _new_xml_name = '%s_%d.xml' % (_file_name,
                                                           _rename_num)
                            _rename_num += 1

                        # 创建文件夹
                        FileTool.create_dir(_save_class_path, exist_ok=True)
                        shutil.copyfile(
                            _annotations['file_path'],
                            os.path.join(_save_class_path, _new_file_name))

                        _is_copy = True

                if _is_copy:
                    # 有复制文件
                    if _is_change_class:
                        # 有修改xml内容
                        _tree.write(os.path.join(_save_class_path,
                                                 _new_xml_name),
                                    encoding='utf-8',
                                    method="xml",
                                    xml_declaration=None)
                    else:
                        shutil.copyfile(
                            _file, os.path.join(_save_class_path,
                                                _new_xml_name))

                # 继续循环处理
                _deal_num += 1

            # 返回结果
            yield [_total, _total, True]
        except:
            print('labelimg_copy_flags_pics error: %s\r\n%s' %
                  (input_path, traceback.format_exc()))
            yield [-1, -1, False]