Fix: AttributeError in OCRProcessor and refactor all relative imports to absolute

This commit is contained in:
2026-07-21 10:34:13 +08:00
parent 97d9d98b4c
commit b3f94d9034
10 changed files with 44 additions and 105 deletions
+11 -7
View File
@@ -21,7 +21,7 @@ from app.core.utils.file_utils import (
save_json save_json
) )
from app.config.settings import ConfigManager from app.config.settings import ConfigManager
from .baidu_ocr import BaiduOCRClient from app.core.ocr.baidu_ocr import BaiduOCRClient
logger = get_logger(__name__) logger = get_logger(__name__)
@@ -112,14 +112,13 @@ class OCRProcessor:
""" """
self.config = config or ConfigManager() self.config = config or ConfigManager()
self.ocr_client = None self.ocr_client = None
self._ensure_ocr_client()
# 修复ConfigParser对象没有get_path方法的问题 # 修复ConfigParser对象没有get_path方法的问题
try: try:
# 获取输入和输出目录 # 获取输入和输出目录
self.input_folder = config.get_path('Paths', 'input_folder', fallback='data/input', create=True) if hasattr(config, 'get_path') else os.path.abspath('data/input') self.input_folder = self.config.get_path('Paths', 'input_folder', fallback='data/input', create=True) if hasattr(self.config, 'get_path') else os.path.abspath('data/input')
self.output_folder = config.get_path('Paths', 'output_folder', fallback='data/output', create=True) if hasattr(config, 'get_path') else os.path.abspath('data/output') self.output_folder = self.config.get_path('Paths', 'output_folder', fallback='data/output', create=True) if hasattr(self.config, 'get_path') else os.path.abspath('data/output')
self.temp_folder = config.get_path('Paths', 'temp_folder', fallback='data/temp', create=True) if hasattr(config, 'get_path') else os.path.abspath('data/temp') self.temp_folder = self.config.get_path('Paths', 'temp_folder', fallback='data/temp', create=True) if hasattr(self.config, 'get_path') else os.path.abspath('data/temp')
# 确保目录存在 # 确保目录存在
os.makedirs(self.input_folder, exist_ok=True) os.makedirs(self.input_folder, exist_ok=True)
@@ -127,13 +126,13 @@ class OCRProcessor:
os.makedirs(self.temp_folder, exist_ok=True) os.makedirs(self.temp_folder, exist_ok=True)
# 获取文件类型列表 # 获取文件类型列表
allowed_extensions_str = config.get('File', 'allowed_extensions', fallback='.jpg,.jpeg,.png,.bmp') allowed_extensions_str = self.config.get('File', 'allowed_extensions', fallback='.jpg,.jpeg,.png,.bmp')
self.file_types = [ext.strip() for ext in allowed_extensions_str.split(',') if ext.strip()] self.file_types = [ext.strip() for ext in allowed_extensions_str.split(',') if ext.strip()]
if not self.file_types: if not self.file_types:
self.file_types = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tif', '.tiff'] self.file_types = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tif', '.tiff']
# 初始化OCR客户端 # 初始化OCR客户端
self.ocr_client = BaiduOCRClient(self.config) self._ensure_ocr_client()
# 记录实际路径 # 记录实际路径
logger.info(f"使用输入目录: {os.path.abspath(self.input_folder)}") logger.info(f"使用输入目录: {os.path.abspath(self.input_folder)}")
@@ -153,6 +152,11 @@ class OCRProcessor:
logger.error(f"初始化OCRProcessor失败: {e}") logger.error(f"初始化OCRProcessor失败: {e}")
raise raise
def _ensure_ocr_client(self):
"""确保OCR客户端已初始化"""
if self.ocr_client is None:
self.ocr_client = BaiduOCRClient(self.config)
def _load_processed_files(self) -> Dict[str, str]: def _load_processed_files(self) -> Dict[str, str]:
""" """
加载已处理的文件记录 加载已处理的文件记录
+2 -2
View File
@@ -13,8 +13,8 @@ from typing import Any, Callable, Dict, List, Optional
from app.config.settings import ConfigManager from app.config.settings import ConfigManager
from app.core.utils.log_utils import get_logger from app.core.utils.log_utils import get_logger
from .ocr_service import OCRService from app.services.ocr_service import OCRService
from .order_service import OrderService from app.services.order_service import OrderService
logger = get_logger(__name__) logger = get_logger(__name__)
+4 -4
View File
@@ -90,7 +90,7 @@ class OrderService:
# 检查是否需要特殊的供应商预处理(如杨碧月) # 检查是否需要特殊的供应商预处理(如杨碧月)
try: try:
from .special_suppliers_service import SpecialSuppliersService from app.services.special_suppliers_service import SpecialSuppliersService
special_service = SpecialSuppliersService(self.config) special_service = SpecialSuppliersService(self.config)
# 尝试识别并预处理(注意:这里不再传入 progress_cb 避免无限递归或重复进度条, # 尝试识别并预处理(注意:这里不再传入 progress_cb 避免无限递归或重复进度条,
@@ -138,7 +138,7 @@ class OrderService:
is_tobacco = df_str.apply(lambda x: x.str.contains('专卖证号|510109104938')).any().any() is_tobacco = df_str.apply(lambda x: x.str.contains('专卖证号|510109104938')).any().any()
if is_tobacco: if is_tobacco:
logger.info("识别到烟草公司订单,执行专用预处理...") logger.info("识别到烟草公司订单,执行专用预处理...")
from .tobacco_service import TobaccoService from app.services.tobacco_service import TobaccoService
tobacco_svc = TobaccoService(self.config) tobacco_svc = TobaccoService(self.config)
return tobacco_svc.preprocess_tobacco_order(file_path) return tobacco_svc.preprocess_tobacco_order(file_path)
@@ -147,7 +147,7 @@ class OrderService:
is_rongcheng = df_str.apply(lambda x: x.str.contains('RCDH')).any().any() is_rongcheng = df_str.apply(lambda x: x.str.contains('RCDH')).any().any()
if is_rongcheng: if is_rongcheng:
logger.info("识别到蓉城易购订单,执行专用预处理...") logger.info("识别到蓉城易购订单,执行专用预处理...")
from .special_suppliers_service import SpecialSuppliersService from app.services.special_suppliers_service import SpecialSuppliersService
special_svc = SpecialSuppliersService(self.config) special_svc = SpecialSuppliersService(self.config)
return special_svc.preprocess_rongcheng_yigou(file_path) return special_svc.preprocess_rongcheng_yigou(file_path)
@@ -164,7 +164,7 @@ class OrderService:
# 检查该列是否有“杨碧月” # 检查该列是否有“杨碧月”
if df_head[handler_col].astype(str).str.contains('杨碧月').any(): if df_head[handler_col].astype(str).str.contains('杨碧月').any():
logger.info("识别到杨碧月订单,执行专用预处理...") logger.info("识别到杨碧月订单,执行专用预处理...")
from .special_suppliers_service import SpecialSuppliersService from app.services.special_suppliers_service import SpecialSuppliersService
special_svc = SpecialSuppliersService(self.config) special_svc = SpecialSuppliersService(self.config)
return special_svc.process_yang_biyue_only(file_path) return special_svc.process_yang_biyue_only(file_path)
+10 -12
View File
@@ -16,15 +16,13 @@ from app.services.ocr_service import OCRService
from app.services.order_service import OrderService from app.services.order_service import OrderService
from app.core.utils.log_utils import get_logger from app.core.utils.log_utils import get_logger
from .logging_ui import add_to_log, init_gui_logger, dispose_gui_logger, GUILogHandler from app.ui.logging_ui import add_to_log, init_gui_logger, dispose_gui_logger, GUILogHandler
from .ui_widgets import ProgressReporter from app.ui.ui_widgets import ProgressReporter
from .error_utils import show_error_dialog, get_error_suggestion from app.ui.error_utils import show_error_dialog, get_error_suggestion
from app.ui.result_previews import show_ocr_result_preview, show_excel_result_preview, show_merge_result_preview
logger = get_logger(__name__) from app.ui.user_settings import add_recent_file
from .result_previews import show_ocr_result_preview, show_excel_result_preview, show_merge_result_preview from app.ui.command_runner import get_running_task, set_running_task
from .user_settings import add_recent_file from app.ui.file_operations import select_file, select_excel_file, validate_unit_price_against_item_data
from .command_runner import get_running_task, set_running_task
from .file_operations import select_file, select_excel_file, validate_unit_price_against_item_data
def _get_missing_barcodes_callback(log_widget): def _get_missing_barcodes_callback(log_widget):
@@ -249,9 +247,9 @@ def batch_process_all_inputs(log_widget, status_bar):
init_gui_logger(log_widget) init_gui_logger(log_widget)
from ..services.batch_service import BatchService from app.services.batch_service import BatchService
from ..config.settings import ConfigManager from app.config.settings import ConfigManager
from .memory_editor import show_memory_editor # noqa: F401 触发 import 顺序 from app.ui.memory_editor import show_memory_editor # noqa: F401 触发 import 顺序
cfg = ConfigManager() cfg = ConfigManager()
svc = BatchService(cfg, missing_barcodes_cb=_get_missing_barcodes_callback(log_widget)) svc = BatchService(cfg, missing_barcodes_cb=_get_missing_barcodes_callback(log_widget))
+3 -3
View File
@@ -7,8 +7,8 @@ import json
import tkinter as tk import tkinter as tk
from tkinter import messagebox, filedialog, scrolledtext from tkinter import messagebox, filedialog, scrolledtext
from .logging_ui import add_to_log from app.ui.logging_ui import add_to_log
from .ui_widgets import center_window from app.ui.ui_widgets import center_window
from app.config.settings import ConfigManager from app.config.settings import ConfigManager
@@ -59,7 +59,7 @@ def ensure_directories():
def clean_cache(log_widget): def clean_cache(log_widget):
"""清除处理缓存""" """清除处理缓存"""
from .command_runner import set_running_task from app.ui.command_runner import set_running_task
try: try:
config = ConfigManager() config = ConfigManager()
processed_record = config.get_path('Paths', 'processed_record', fallback='data/processed_files.json') processed_record = config.get_path('Paths', 'processed_record', fallback='data/processed_files.json')
+11 -11
View File
@@ -11,29 +11,29 @@ from tkinter import messagebox, filedialog, scrolledtext
from app.config.settings import ConfigManager from app.config.settings import ConfigManager
from app.core.utils.log_utils import set_log_level from app.core.utils.log_utils import set_log_level
from .theme import THEMES, get_theme_mode, set_theme_mode, create_modern_button, create_card_frame from app.ui.theme import THEMES, get_theme_mode, set_theme_mode, create_modern_button, create_card_frame
from .logging_ui import add_to_log, poll_log_queue from app.ui.logging_ui import add_to_log, poll_log_queue
from .ui_widgets import StatusBar, ToolTip from app.ui.ui_widgets import StatusBar, ToolTip
from .user_settings import ( from app.ui.user_settings import (
load_user_settings, save_user_settings, refresh_recent_list_widget, load_user_settings, save_user_settings, refresh_recent_list_widget,
_extract_path_from_recent_item, clear_recent_files, RECENT_LIST_WIDGET, _extract_path_from_recent_item, clear_recent_files, RECENT_LIST_WIDGET,
) )
from .file_operations import ( from app.ui.file_operations import (
ensure_directories, open_result_directory, clean_cache, ensure_directories, open_result_directory, clean_cache,
clean_data_files, clean_result_files, clean_data_files, clean_result_files,
) )
from .action_handlers import ( from app.ui.action_handlers import (
process_single_image_with_status, run_pipeline_directly, process_single_image_with_status, run_pipeline_directly,
batch_ocr_with_status, batch_process_orders_with_status, batch_ocr_with_status, batch_process_orders_with_status,
merge_orders_with_status, process_excel_file_with_status, merge_orders_with_status, process_excel_file_with_status,
process_dropped_file, batch_process_all_inputs, process_dropped_file, batch_process_all_inputs,
) )
from .memory_editor import show_memory_editor from app.ui.memory_editor import show_memory_editor
from .config_dialog import show_config_dialog from app.ui.config_dialog import show_config_dialog
from .barcode_editor import edit_barcode_mappings from app.ui.barcode_editor import edit_barcode_mappings
from .shortcuts import bind_keyboard_shortcuts from app.ui.shortcuts import bind_keyboard_shortcuts
from app.core.utils.dialog_utils import show_cloud_sync_dialog from app.core.utils.dialog_utils import show_cloud_sync_dialog
from .db_viewer import show_db_viewer from app.ui.db_viewer import show_db_viewer
def _init_window(): def _init_window():
+1 -1
View File
@@ -5,7 +5,7 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
from .theme import THEMES, get_theme_mode from app.ui.theme import THEMES, get_theme_mode
class StatusBar(tk.Frame): class StatusBar(tk.Frame):
+1 -1
View File
@@ -41,7 +41,7 @@ version = 2026.07.20.2105
base_url = https://gitea.94kan.cn base_url = https://gitea.94kan.cn
owner = houhuan owner = houhuan
repo = yixuan-sync-data repo = yixuan-sync-data
token = 50b61e43a141d606ae2529cd1755bc666d800e08 token = 149f3f64edad2a3a25d94a054ea699f3d28f7294
[WebAuth] [WebAuth]
username = admin username = admin
+1 -1
View File
@@ -38,5 +38,5 @@ item_data = 商品资料.xlsx
base_url = https://gitea.94kan.cn base_url = https://gitea.94kan.cn
owner = houhuan owner = houhuan
repo = yixuan-sync-data repo = yixuan-sync-data
token = 50b61e43a141d606ae2529cd1755bc666d800e08 token = 149f3f64edad2a3a25d94a054ea699f3d28f7294
-63
View File
@@ -1,63 +0,0 @@
import os
import sys
import json
from pathlib import Path
# Add project root to path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from app.core.utils.cloud_sync import GiteaSync
from app.config.settings import ConfigManager
def sync_all():
config = ConfigManager()
sync = GiteaSync.from_config(config)
if not sync:
print("Error: Gitea configuration missing in config.ini")
sys.exit(1)
# Sync files defined in dialog_utils.py (re-implementing the logic here)
SYNC_FILES = [
{"name": "条码映射", "remote": "barcode_mappings.json", "local": "config/barcode_mappings.json", "type": "json"},
{"name": "供应商配置", "remote": "suppliers_config.json", "local": "config/suppliers_config.json", "type": "json"},
{"name": "商品资料", "remote": "templates/商品资料.xlsx", "local": "templates/商品资料.xlsx", "type": "binary"},
{"name": "采购单模板", "remote": "templates/银豹-采购单模板.xls", "local": "templates/银豹-采购单模板.xls", "type": "binary"},
{"name": "商品记忆库 (DB)", "remote": "product_cache.db", "local": "data/product_cache.db", "type": "binary"},
]
print(f"Starting sync to {sync.base_url}/{sync.owner}/{sync.repo}...")
success_count = 0
for entry in SYNC_FILES:
local_path = entry["local"]
remote_path = entry["remote"]
name = entry["name"]
if not os.path.exists(local_path):
print(f"Skipping {name}: Local file not found at {local_path}")
continue
print(f"Pushing {name} ({local_path}) -> {remote_path}...")
try:
if entry["type"] == "json":
with open(local_path, "r", encoding="utf-8") as f:
data = json.load(f)
# Get current SHA to update
sha = sync.file_exists(remote_path)
result = sync.push_json(remote_path, data, f"Sync: {name}", sha=sha)
else:
result = sync.push_binary(remote_path, local_path, f"Sync: {name}")
if result:
print(f"Successfully synced {name}")
success_count += 1
else:
print(f"Failed to sync {name}")
except Exception as e:
print(f"Error syncing {name}: {e}")
print(f"\nSync finished. {success_count}/{len(SYNC_FILES)} files synced.")
if __name__ == "__main__":
sync_all()