Problem G: Keywords Search

关键词搜索算法详解
本文深入解析了一种关键词搜索算法,该算法能够在大规模文本中高效地查找关键词出现的次数。通过具体的代码实现,展示了如何处理大量数据,包括关键词的匹配过程和优化技巧。适合对文本检索和算法优化感兴趣的读者。

Problem G: Keywords Search
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 10 Solved: 6
[Submit][Status][Web Board] [Edit] [TestData]
Description
n the modern time, Search engine came into the life of everybody like Google, Baidu, etc.
Wiskey also wants to bring this feature to his image retrieval system.
Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched.
To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match.

Input
First line will contain one integer means how many cases will follow by.
Each case will contain two integers N means the number of keywords and N keywords follow. (N <= 10000)
Each keyword will only contains characters 'a'-'z', and the length will be not longer than 50.
The last line is the description, and the length will be not longer than 1000000.

Output
Print how many keywords are contained in the description.

Sample Input
1
5
she
he
say
shr
her
yasherhs
Sample Output
3

#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
using namespace std;
char b[1000000];//存放句子
int main()
{
    int n,gjc,i,j,len,lena,k,ji,count;
    string a[10000];
while(scanf("%d",&n)!=EOF)  //总的测试的个数
{
    while(n--)
    {
        cin>>gjc;//关键词的个数
    for(i=0;i<gjc;i++)
    {
        cin>>a[i];
    }
    getchar();
    gets(b);
    len=strlen(b);
    count=0;
    for(i=0;i<gjc;i++)
    {
        lena=a[i].size();
        for(j=0;j<len;j++)
        {
            ji=0;   
            for(k=0;k<lena;k++)
            {
                if(b[j]!=a[i][k])
                {
                    j=j-ji;
                    break;
                }
                else
                {
                    ji++;
                    j++;
                }
            }
            if(ji==lena)
            {
                count++;
                j=j-lena;
            }
        }
    }
    cout<<count<<endl;
}
}
    return 0;
}

 

转载于:https://www.cnblogs.com/NYNU-ACM/p/4236893.html

请问这个类中的get_current_activity()方法,在其他用例文件,都有那些调用方式,方法如下: # !/usr/bin/env python # -*- coding: utf-8 -*- from typing import Optional import threading import subprocess import openpyxl import re from hytest import * import time import random from appium import webdriver from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import WebDriverException import os import zipfile import tempfile import shutil from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException import cv2 import numpy as np from lib.common import ADBHelper, get_app_driver, get_sup_app_driver, Operate from selenium.webdriver.common.by import By from PIL import Image import statistics from openpyxl import load_workbook from hytest import * from appium import webdriver import sys import os import re import time import platform import subprocess from appium.webdriver.common.multi_action import MultiAction from appium.webdriver.common.touch_action import TouchAction from selenium.webdriver.common.by import By from appium.webdriver.common.appiumby import AppiumBy from multiprocessing.dummy import Process from selenium.webdriver import ActionChains import yaml, ruamel.yaml import hytest from selenium.common.exceptions import ( TimeoutException, ElementNotInteractableException, StaleElementReferenceException, NoSuchElementException, WebDriverException ) ############################################################################### CURRENT_TIME = datetime.now().strftime('%Y%m%d%H%M%S') GLOBAL_REPORT_FOLDER = f"./reports/task_{CURRENT_TIME}_report" os.makedirs(GLOBAL_REPORT_FOLDER, exist_ok=True) class AppiumAutomationUtils: def __init__(self, device_name, platform_version, is_large_install=False): self.is_large_install = is_large_install # 根据是否为大文件安装场景设置超时 self.timeout = 300 if is_large_install else 30 self.desired_caps = { "platformName": "Android", "deviceName": device_name, "appium:platformVersion": platform_version, "appium:automationName": "UiAutomator2", "newCommandTimeout": self.timeout, "appium:adbExecTimeout": 60000 } self.driver = webdriver.Remote('http://localhost:4723/wd/hub', self.desired_caps) self.last_activity_time = time.time() self.folder_name = GLOBAL_REPORT_FOLDER self.report_file = os.path.join(self.folder_name, "report.xlsx") self.report_files = os.path.join(self.folder_name, "downapp_report.xlsx") self.report_checkfile = os.path.join(self.folder_name, "checkapp_report.xlsx") def restart_appium_server(self): # 启动Appium服务 start_cmd = 'appium -a 127.0.0.1 -p 4723 --session-override --allow-insecure=adb_shell' subprocess.Popen(start_cmd, shell=True) time.sleep(10) @property def get_driver(self): return self.driver def start_app(self, package_name): crash_count, anr_count = 0, 0 flower_count, black_count, white_count = 0, 0, 0 try: self.driver.activate_app(package_name) time.sleep(5) start_time = time.time() while time.time() - start_time < 10: status = self.driver.query_app_state(package_name) if status != 2: if self.check_app_crash(): crash_count += 1 break if self.check_app_anr(): anr_count += 1 is_flower, is_black, is_white = self.verify_screen() if is_flower: flower_count += 1 if is_black: black_count += 1 if is_white: white_count += 1 time.sleep(3) except Exception as e: if self.check_app_crash(): crash_count += 1 elif self.check_app_anr(): anr_count += 1 raise RuntimeError(f"应用启动失败: {str(e)}") return crash_count, anr_count, flower_count, black_count, white_count def check_screen(self): timestamp = int(time.time() * 1000) temp_dir = "D:/problem/temp_screenshots/" os.makedirs(temp_dir, exist_ok=True) temp_path = os.path.join(temp_dir, f"temp_{timestamp}.png") try: screenshot = self.driver.get_screenshot_as_png() with open(temp_path, 'wb') as f: f.write(screenshot) except Exception as e: return False, False, False, None img_cv = cv2.imread(temp_path) if img_cv is None: os.remove(temp_path) return False, False, False, None height, width, _ = img_cv.shape total_pixels = height * width aspect_ratio = width / height if 1.5 <= aspect_ratio <= 2.0: num_rows, num_cols = 4, 8 elif aspect_ratio > 2.0: num_rows, num_cols = 3, 12 else: num_rows, num_cols = 3, 6 block_h, block_w = height // num_rows, width // num_cols block_list = [] FEATURE_THRESHOLDS = {'variance': 150, 'entropy': 2.2, 'edge_density': 0.08, 'contrast': 40} for row in range(num_rows): for col in range(num_cols): y_start = max(0, row * block_h) y_end = min(height, (row + 1) * block_h) x_start = max(0, col * block_w) x_end = min(width, (col + 1) * block_w) block = img_cv[y_start:y_end, x_start:x_end] gray = cv2.cvtColor(block, cv2.COLOR_BGR2GRAY) h, w = gray.shape block_area = h * w variance = np.var(gray) hist = cv2.calcHist([gray], [0], None, [256], [0, 256]).flatten() prob = hist / (block_area + 1e-7) entropy = -np.sum(prob * np.log(prob + 1e-7)) edges = cv2.Canny(gray, 50, 150) edge_pixels = np.count_nonzero(edges) edge_density = edge_pixels / block_area contrast = np.max(gray) - np.min(gray) block_list.append({ 'variance': variance, 'entropy': entropy, 'edge_density': edge_density, 'contrast': contrast, 'area': block_area, 'coords': (x_start, y_start, x_end, y_end) }) # 原花屏检测逻辑 all_variances = [b['variance'] for b in block_list] all_entropies = [b['entropy'] for b in block_list] global_variance_mean = statistics.mean(all_variances) global_entropy_mean = statistics.mean(all_entropies) dynamic_thresholds = { 'variance': global_variance_mean - 1.5 * np.std(all_variances), 'entropy': global_entropy_mean - 1.5 * np.std(all_entropies) } flower_blocks = [b for b in block_list if sum([b['variance'] < dynamic_thresholds['variance'], b['entropy'] < dynamic_thresholds['entropy'], b['edge_density'] > FEATURE_THRESHOLDS['edge_density'], b['contrast'] < FEATURE_THRESHOLDS['contrast']]) >= 3 and b['area'] >= (total_pixels * 0.01)] is_flower_screen = (sum(b['area'] for b in flower_blocks) / total_pixels) > 0.1 # 黑白屏检测逻辑 img_pil = Image.open(temp_path).convert('RGBA') color, total = (0, 0, 0), 0 for count, (r, g, b, a) in img_pil.getcolors(img_pil.size[0] * img_pil.size[1]): if a != 0: color = (color[0] + r * count, color[1] + g * count, color[2] + b * count) total += count if total > 0: dominant_color = (int(color[0] / total), int(color[1] / total), int(color[2] / total)) mean_brightness = statistics.mean(dominant_color) is_black_screen = mean_brightness < 10 is_white_screen = mean_brightness > 254 else: is_black_screen, is_white_screen = True, False return is_flower_screen, is_black_screen, is_white_screen, temp_path def verify_screen(self): """ 二次检测屏幕异常,减少检测屏幕异常误报率 """ save_dir = "D:/problem/problemphoto/" os.makedirs(save_dir, exist_ok=True) temp_files = [] # 第一次检测 first_flower, first_black, first_white, first_temp = self.check_screen() if first_temp: temp_files.append(first_temp) if not (first_flower or first_black or first_white): self.clean_temp_files(temp_files) return False, False, False time.sleep(0.8) second_flower, second_black, second_white, second_temp = self.check_screen() if second_temp: temp_files.append(second_temp) # 最终判定:两次同类异常才保存 final_flower = first_flower and second_flower final_black = first_black and second_black final_white = first_white and second_white # 截图保存逻辑 if final_flower or final_black or final_white: timestamp = time.strftime("%Y%m%d_%H%M%S", time.localtime()) anomaly_types = [] if final_flower: anomaly_types.append("flower") if final_black: anomaly_types.append("black") if final_white: anomaly_types.append("white") filename = f"{timestamp}_{'_'.join(anomaly_types)}.png" # 保存二次检测的临时截图到目标目录 shutil.copy(second_temp, os.path.join(save_dir, filename)) self.log_error() # 清理所有临时文件(无论是否保存) self.clean_temp_files(temp_files) return final_flower, final_black, final_white def clean_temp_files(self, temp_files): """ 辅助方法:安全删除临时文件 """ for path in temp_files: if os.path.exists(path): try: os.remove(path) except Exception as e: pass def install_app(self, apk_path): """使用 os.system 执行 adb 命令""" command = f"adb install -d -r -g {apk_path}" exit_code = os.system(command) if exit_code == 0: print("应用安装成功") else: print("安装失败") def random_operation(self, duration,package_name,timeout=25): crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count = 0, 0, 0, 0, 0 # 获取设备屏幕尺寸 screen = self.driver.get_window_size() screen_width = screen['width'] screen_height = screen['height'] start_time = time.time() last_operation_time = time.time() # 记录最后一次成功操作的时间 while time.time() - start_time < duration: # 检测全局超时 if time.time() - last_operation_time > timeout: print(f"操作超时({timeout}秒无响应),结束测试") break #驻留检测 try: currentt_package = self.get_current_package().lower() target_package = package_name.lower() if currentt_package != target_package: print(f"当前包名不匹配目标!!!") self.driver.activate_app(package_name) time.sleep(3) last_operation_time = time.time() except Exception as e: print(f"驻留检测失败:{e}") try: # 随机选择操作类型(点击/滑动/输入) operation = random.choice(['click', 'swipe', 'input']) if operation == 'click': # 随机坐标点击 x = random.randint(0, screen_width) y = random.randint(0, screen_height) self.driver.tap([(x, y)], duration=50) time.sleep(0.3) elif operation == 'swipe': # 随机方向滑动(上下左右) direction = random.choice(['up', 'down', 'left', 'right']) if direction == 'up': self.driver.swipe(screen_width // 2, screen_height * 3 // 4, screen_width // 2, screen_height // 4, 500) elif direction == 'down': self.driver.swipe(screen_width // 2, screen_height // 4, screen_width // 2, screen_height * 3 // 4, 500) elif direction == 'left': self.driver.swipe(screen_width * 3 // 4, screen_height // 2, screen_width // 4, screen_height // 2, 500) elif direction == 'right': self.driver.swipe(screen_width // 4, screen_height // 2, screen_width * 3 // 4, screen_height // 2, 500) time.sleep(0.5) elif operation == 'input': input_elements = self.driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.EditText") if input_elements: input_element = random.choice(input_elements) input_element.click() random_digits = ''.join(str(random.randint(0, 9)) for _ in range(random.randint(1, 10))) input_element.send_keys(random_digits) time.sleep(0.8) if self.check_app_crash(): crash_count += 1 if self.check_app_anr(): anr_count += 1 is_flower, is_black, is_white = self.verify_screen() if is_flower: flower_screen_count += 1 if is_black: black_screen_count += 1 if is_white: white_screen_count += 1 last_operation_time = time.time() except Exception as e: pass return (crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count) def restart_app(self, times, package_name): """ 带重试逻辑的应用重启方法,支持 Activity 动态获取 :param times: 重启循环次数 :param package_name: 目标应用包名 """ crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count = 0, 0, 0, 0, 0 for _ in range(times): try: # 步骤1:终止应用并等待 self.driver.terminate_app(package_name) time.sleep(5) # 步骤2:尝试激活应用 self.driver.activate_app(package_name) time.sleep(5) except Exception as e: self.last_activity_time = time.time() retry_count = 0 while retry_count < 2: try: self.driver.activate_app(package_name) time.sleep(5) current_package = self.driver.current_package if package_name == current_package: break else: print(f"第{retry_count + 1}次启动未启动成功") retry_count += 1 time.sleep(5) except Exception as retry_e: continue if self.check_app_crash(): crash_count += 1 if self.check_app_anr(): anr_count += 1 is_flower, is_black, is_white = self.verify_screen() if is_flower: flower_screen_count += 1 if is_black: black_screen_count += 1 if is_white: white_screen_count += 1 return crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count def uninstall_app(self, package_name): try: self.driver.terminate_app(package_name) os.system(f"adb uninstall {package_name}") return True except: return False pass def generate_report(self, app_name, package_name, crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count, app_version, install_result, start_result,uninstall_result): if not os.path.exists(self.report_file): wb = openpyxl.Workbook() sheet = wb.active sheet.append(["序号", "应用名称", "包名", "应用版本号", "安装应用", "启动应用", "闪退次数", "ANR次数", "花屏次数", "黑屏次数", "白屏次数", "卸载结果","统计"]) row_number = 1 else: wb = openpyxl.load_workbook(self.report_file) sheet = wb.active row_number = len(sheet['A']) install_result_str = "成功" if install_result else "失败" start_result_str = "成功" if start_result else "失败" uninstall_result_str = "成功" if uninstall_result else "失败" has_failure = (not install_result) or (not start_result) or \ (crash_count > 0 or anr_count > 0 or flower_screen_count > 0 or black_screen_count > 0 or white_screen_count > 0) status = "fail" if has_failure else "pass" sheet.append([ row_number, app_name, package_name, app_version, install_result_str, start_result_str, crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count,uninstall_result_str, status ]) # 保存文件 wb.save(self.report_file) def generate_report_even_failed(self, app_name, package_name, crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count, app_version, install_result, start_result,uninstall_result): try: self.generate_report(app_name, package_name, crash_count, anr_count, flower_screen_count, black_screen_count, white_screen_count, app_version, install_result, start_result,uninstall_result) except Exception as e: print(f"生成报告时出错:{str(e)}") def log_error(self): current_timestamp = datetime.now().strftime('%Y%m%d%H%M%S') log_folder = f"D:/problem/logs/{current_timestamp}" os.makedirs(log_folder, exist_ok=True) adb_pull_command = f"pull /data/log/hilogs {log_folder}" ADBHelper().adb(adb_pull_command) time.sleep(10) adb_pull_command = f"pull /data/log/dropbox {log_folder}" ADBHelper().adb(adb_pull_command) time.sleep(10) def quit_driver(self): self.driver.quit() def click_element_by_texts(self, texts): """循环匹配文本点击页面文本元素""" screenshot_dir = "D:/problem/clickscreenshot" if not os.path.exists(screenshot_dir): os.makedirs(screenshot_dir) print(f"已创建截图文件夹:{screenshot_dir}") for text in texts: try: element = self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, f'new UiSelector().text("{text}")') if element.is_enabled() and element.is_displayed(): element.click() return True except: continue else: screenshot_name = f"{int(time.time())}.png" screenshot_path = os.path.join(screenshot_dir, screenshot_name) self.driver.save_screenshot(screenshot_path) return False def get_app_version(self, package_name): """获取当前应用版本""" try: result = os.popen(f"adb shell dumpsys package {package_name} | findstr versionName").read().strip() if result: parts = result.split('=') if len(parts) > 1: return parts[1] return "未知版本" except: return "未知版本" def check_app_crash(self): """检测应用是否闪退""" try: current_activity = self.get_current_activity() home_activity = "com.huawei.android.launcher.unihome.UniHomeLauncher" if current_activity == home_activity: print(f"应用发生闪退!!!") self.scrennphoto_problem(problem_type="crash") self.log_error() return True else: return False except Exception as e: print(f"检测闪退时出错: {e}") return None def scrennphoto_problem(self, problem_type: str): """封装:截图并记录日志(根据问题类型生成不同目录)""" base_dir = "D:/problem/" screenshot_dir = os.path.join(base_dir, f"{problem_type}photo/") os.makedirs(screenshot_dir, exist_ok=True) timestamp = time.strftime('%Y%m%d_%H%M%S') screenshot_path = os.path.join(screenshot_dir, f"{problem_type}_{timestamp}.png") if self.driver.save_screenshot(screenshot_path): print(f"截图保存成功:{screenshot_path}") else: print(f"截图保存失败:{screenshot_path}") # def check_app_anr(self): # """判断当前页面是否存在ANR问题""" # try: # # 执行ANR检测命令 # result = subprocess.check_output( # "adb shell logcat -d | grep -i ANR", # shell=True, # text=True, # stderr=subprocess.STDOUT # ) # # 判断是否存在ANR # if "ANR" in result: # self.scrennphoto_problem(problem_type="anr") # self.log_error() # return True # return True # except Exception as e: # return False def check_app_anr(self): """判断当前页面是否存在ANR问题""" anr_keywords = ["无响应", "关闭应用", "是否将其关闭", "等待"] try: has_anr_screen = False for keyword in anr_keywords: elements = self.driver.find_elements( by=AppiumBy.XPATH, value=f"//*[contains(@text, '{keyword}')]" ) if elements: has_anr_screen = True break if has_anr_screen: print(f"检测到ANR:日志存在ANR记录且屏幕显示无响应提示") self.scrennphoto_problem(problem_type="anr") self.log_error() return True else: return False except Exception as e: print(f"ANR检测异常:{str(e)}") return False def is_target_activity(self, package_name, activity): """判断当前 Activity 是否属于目标应用""" return activity and activity.startswith(package_name) def get_current_activity(self): """获取当前Android设备的Activity名称""" try: # 执行ADB命令获取窗口信息 command = "adb shell dumpsys window | findstr mCurrentFocus" result = subprocess.check_output(command, shell=True, text=True, timeout=5) except subprocess.CalledProcessError: return "错误:ADB命令执行失败,请检查设备连接" except subprocess.TimeoutExpired: return "错误:命令执行超时,请确认ADB服务正常" except Exception as e: return f"错误:{str(e)}" if not result.strip(): return "提示:未获取到窗口信息,请先打开一个应用" # 用正则表达式匹配/后面的内容 match = re.search(r'/([^ }]+)', result) if match: return match.group(1) else: return "提示:未找到Activity名称,输出格式可能不一致" def get_current_package(self): """验证应用是否下载成功""" current_package = self.driver.current_package # 获取当前包名 return current_package def pull_download_apk(self): """ 从Android设备提取指定包名的APK文件 """ # 固定参数设置 adb_path = 'adb' output_dir = 'D:\\apk' # 获取当前应用包名 try: package_name = self.get_current_package() if not package_name: raise ValueError("无法获取当前应用包名") except Exception as e: raise RuntimeError(f"获取包名失败: {str(e)}") # 确保输出目录存在 # os.makedirs(output_dir, exist_ok=True) if not os.path.exists(output_dir): os.makedirs(output_dir) # 获取包安装路径 cmd_get_path = f"{adb_path} shell pm path {package_name}" try: result = subprocess.run(cmd_get_path, capture_output=True, text=True, shell=True, timeout=30) if result.returncode != 0: raise RuntimeError(f"获取包路径失败: {result.stderr.strip()}") # 解析输出结果 output = result.stdout.strip() if not output: raise ValueError(f"未找到包名为 '{package_name}' 的应用") # 提取APK路径 (取第一个路径) apk_paths = re.findall(r'package:(.+)', output) if not apk_paths: raise ValueError(f"无法解析包路径: {output}") device_path = apk_paths[0].strip() print(f"设备路径: {device_path}") except subprocess.TimeoutExpired: raise RuntimeError("获取包路径超时,请检查设备连接") # 创建本地文件名和路径 local_filename = f"{package_name}.apk" local_path = os.path.join(output_dir, local_filename) # 执行pull命令 cmd_pull = f"{adb_path} pull {device_path} \"{local_path}\"" try: result = subprocess.run(cmd_pull, capture_output=True, text=True, shell=True, timeout=60) if result.returncode != 0: raise RuntimeError(f"提取APK失败: {result.stderr.strip()}") except subprocess.TimeoutExpired: raise RuntimeError("提取APK超时,文件可能过大") # 验证文件是否成功提取 if not os.path.exists(local_path): raise FileNotFoundError(f"文件提取失败: {local_path}") print(f"成功提取APK到: {local_path}") return local_path def check_downapp_verify(self, package_name, app_name): """检测应用是否下载正确(返回字典)""" try: current_package = self.get_current_package().lower() expected_package = package_name.lower() # 构造返回结果(包含状态、应用名、包名、原因) result = { "status": None, "app_name": app_name, "package_name": package_name, "reason": "" } if current_package == expected_package: return True else: result["status"] = False result["reason"] = f"下载应用包名不符" return result except Exception as e: return { "status": None, "app_name": app_name, "package_name": package_name, "reason": f"检测应用时出错: {e}" } def downapp_report(self, app_name, package_name, download_result, pullapk_result, remark=None): if not os.path.exists(self.report_files): wb = openpyxl.Workbook() sheet = wb.active sheet.append(["应用名称", "包名", "下载结果", "上传结果", "统计", "备注"]) row_number = 1 else: wb = openpyxl.load_workbook(self.report_files) sheet = wb.active row_number = len(sheet['A']) download_result_str = "成功" if download_result else "失败" pullapk_result_str = "成功" if pullapk_result else "失败" has_failure = (not download_result) or (not pullapk_result) status = "fail" if has_failure else "pass" sheet.append([ app_name, package_name, download_result_str, pullapk_result_str, status, remark ]) wb.save(self.report_files) def click_element_with_swipe(self,driver, target_id, target_text, timeout=10, max_swipe=0): """ 定位并点击同时满足ID和文本条件的元素(未找到时下滑重试),返回操作结果 """ uiautomator_selector = f'new UiSelector().resourceId("{target_id}").textContains("{target_text}")' located = False for attempt in range(max_swipe + 1): try: element = WebDriverWait(driver, timeout).until( EC.element_to_be_clickable(("-android uiautomator", uiautomator_selector)) ) element.click() print(f"成功点击元素(ID={target_id}, 文本={target_text})") located = True break except TimeoutException: if attempt < max_swipe: print(f"第{attempt + 1}次定位超时,尝试下滑...") self.swipe_down(driver) else: print(f"已尝试{max_swipe + 1}次,未找到符合条件的元素") except Exception as e: print(f"操作失败,原因:{str(e)}") located = False break return located def swipe_down(self, driver, duration=500, swipe_times=1): """ 动态计算坐标实现下滑(页面向下滚动) """ # 获取屏幕尺寸 window_size = driver.get_window_size() x = window_size["width"] y = window_size["height"] # x, y = ADBHelper().get_phone_size() x1 = x * 0.5 y1 = y * 0.9 x2 = x * 0.5 y2 = y * 0.2 for i in range(swipe_times): driver.swipe(x1, y1, x2, y2, duration) print(f"第{i + 1}/{swipe_times}次下滑操作完成,等待页面加载...") driver.implicitly_wait(3) print(f"全部{swipe_times}次下滑操作执行完毕") def swipe_up(self, driver, duration=500, swipe_times=1): """ 动态计算坐标实现上滑(页面向上滚动) """ # 获取屏幕尺寸 # window_size = driver.get_window_size() # x = window_size["width"] # y = window_size["height"] x, y = ADBHelper().get_phone_size() x1 = x * 0.5 y1 = y * 0.2 x2 = x * 0.5 y2 = y * 0.9 for i in range(swipe_times): driver.swipe(x1, y1, x2, y2, duration) print(f"第{i + 1}/{swipe_times}次上滑操作完成,等待页面加载...") driver.implicitly_wait(3) print(f"全部{swipe_times}次上滑操作执行完毕") def go_back(self, driver, times=1, interval=3): """ :param times: 要点击返回键的次数(需≥0,默认1次) :param interval: 每次点击的间隔时间(秒,默认3秒) """ # 执行返回键点击 try: for i in range(times): driver.press_keycode(4) print(f"已点击返回键(第{i + 1}/{times}次)") time.sleep(interval) return True except WebDriverException as e: print(f"返回键操作失败,原因:{str(e)}") pass return False def wait_element_click(self,driver, locator, timeout=600): """ 等待元素可点击后尝试点击,返回点击是否成功 """ try: element = WebDriverWait(driver, timeout).until( EC.element_to_be_clickable(locator) ) except Exception as e: return False try: element.click() return True except (ElementNotInteractableException, StaleElementReferenceException, WebDriverException): return False def download_AG_app(self, driver, app_name, package_name): market_package = "com.huawei.appmarket" try: for i in range(2): driver.press_keycode(187) try: driver.find_element(By.ID, 'com.huawei.android.launcher:id/clear_all_recents_image_button').click() except: pass driver.press_keycode(3) except: pass time.sleep(10) driver.activate_app(market_package) time.sleep(10) self.click_element_with_swipe(driver, target_id='com.huawei.appmarket:id/enter_button', target_text='暂不安装') self.click_element_with_swipe(driver, target_id='android:id/button2', target_text='以后再说') self.swipe_up(driver, swipe_times=3) driver.find_element(By.ID, 'com.huawei.appmarket:id/fixed_search_view').click() time.sleep(3) src_text = driver.find_element(By.ID, "com.huawei.appmarket:id/search_src_text") src_text.set_text(app_name) time.sleep(3) driver.find_element(By.ID, 'com.huawei.appmarket:id/hwsearchview_search_text_button').click() time.sleep(3) result1 = self.click_element_with_swipe(driver, target_id='com.huawei.appmarket:id/ItemTitle',target_text=f'{app_name}', max_swipe=3) # 可以在应用市场搜索到该应用 if result1 == True: time.sleep(5) # 场景1:应用未安装 result2= self.click_element_with_swipe(driver, target_id='com.huawei.appmarket:id/hwprogressbutton_percentage_text_view',target_text='安装') if result2 == True: open_text = (AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("打开")') result3=self.wait_element_click(driver, open_text) # 应用规定时间内安装完成 if result3 == True: time.sleep(5) self.click_element_with_swipe(driver,target_id='com.android.permissioncontroller:id/permission_allow_button',target_text='允许') else: # 下载超时&开发者原因暂不支持下载 self.click_element_with_swipe(driver,target_id='com.huawei.appmarket:id/hwprogressbutton_percentage_text_view',target_text='%') self.go_back(driver, times=3) return { "status": "notime", "app_name": app_name, "package_name": package_name, "reason": f"{app_name}下载超时&开发者原因暂不支持下载 " } # 场景2:应用已存在 else: time.sleep(30) result4 = self.click_element_with_swipe(driver,target_id='com.huawei.appmarket:id/hwprogressbutton_percentage_text_view', target_text='打开') if result4 == True: time.sleep(5) self.click_element_with_swipe(driver, target_id='com.android.permissioncontroller:id/permission_allow_button',target_text='允许') else: pass else: failure_info = { "status": "fail1", "app_name": app_name, "package_name": package_name, "reason": f"应用市场未找到应用:{app_name}" } self.go_back(driver, times=3) return failure_info def check_apk_architecture(self,apk_path: str, app_name: str, package_name: str) -> dict: """ 检测APK架构并返回指定格式的结果字典 :param apk_path: APK文件路径 :param app_name: 应用名称(需外部传入) :param package_name: 应用包名(需外部传入) :return: 包含检测状态的字典(check_info) """ # 初始化默认结果(检测失败状态) check_info = { "status": "fail", "app_name": app_name, "package_name": package_name, "remark": "检测失败" } x64_archs = {'arm64-v8a', 'x86_64', 'mips64'} x32_archs = {'armeabi', 'armeabi-v7a', 'x86', 'mips'} detected_64 = set() detected_32 = set() try: if not os.path.isfile(apk_path): check_info["remark"] = "检测失败:APK文件不存在" return check_info with zipfile.ZipFile(apk_path, 'r') as zip_ref: all_members = zip_ref.namelist() for member in all_members: member = member.replace('\\', '/') if member.startswith("lib/"): lib_subpath = member.split("lib/")[1].split('/') if len(lib_subpath) < 1: continue arch_dir = lib_subpath[0].lower() if not arch_dir: continue if arch_dir in x32_archs: detected_32.add(arch_dir) elif arch_dir in x64_archs: detected_64.add(arch_dir) # 判断检测结果 has_64bit = len(detected_64) > 0 if has_64bit: # 64位检测成功 check_info.update({ "status": "success", "remark": "应用64位,已保留" }) else: # 32位检测成功 os.remove(apk_path) check_info.update({ "status": "success", "remark": "应用32位,已删除" }) except Exception as e: check_info["remark"] = f"检测失败:{e}" return check_info def checkapp_report(self, app_name, package_name, check_result): if not os.path.exists(self.report_checkfile): wb = openpyxl.Workbook() sheet = wb.active sheet.append(["应用名称", "包名", "检测结果"]) row_number = 1 else: wb = openpyxl.load_workbook(self.report_checkfile) sheet = wb.active row_number = len(sheet['A']) sheet.append([ app_name, package_name, check_result ]) wb.save(self.report_checkfile)
07-26
import os import re import json import logging import traceback import pandas as pd from datetime import datetime from sqlalchemy import create_engine, inspect from sqlalchemy.engine import Engine from dotenv import load_dotenv # 初始化日志 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) # 加载环境变量 load_dotenv() # 输出文件配置 OUTPUT_DIR = r"D:\AEB Management\output" OUTPUT_FILE = "processed_data.csv" os.makedirs(OUTPUT_DIR, exist_ok=True) # ------------------ 配置加载 ------------------ def load_config(config_path): """加载 JSON 配置文件为字典""" try: with open(config_path, "r", encoding="utf-8") as file: config = json.load(file) logging.info(f"成功加载配置文件: {config_path}") return config except Exception as e: logging.error(f"加载配置文件失败: {str(e)}") logging.error(traceback.format_exc()) return {} # 返回空字典避免后续错误 # ------------------ 数据列处理 ------------------ def add_columns(dataframe, bsw_staff_set, progress_callback=None): """根据业务规则动态添加新列""" current_time = pd.Timestamp.now() total_rows = len(dataframe) # 添加基础列 dataframe['HorizontalExpansion PN'] = None # 1. KOCHI_PL 项目处理逻辑 kochi_condition_include = ( (dataframe['Project key'] == 'KOCHI_PL') & (dataframe['Labels'].str.contains('CheckItOnOtherProject', na=False)) ) kochi_condition_exclude = ( (dataframe['Project key'] == 'KOCHI_PL') & (~dataframe['Labels'].str.contains('CheckItOnOtherProject', na=False)) ) if kochi_condition_include.any(): extracted_projects = dataframe.loc[kochi_condition_include, 'Summary'].str.extract(r'(P\d{5})', expand=False) dataframe.loc[kochi_condition_include, 'HorizontalExpansion PN'] = extracted_projects valid_extraction = extracted_projects.notna() dataframe.loc[kochi_condition_include & valid_extraction, 'Project key'] = extracted_projects[valid_extraction] if kochi_condition_exclude.any(): dataframe.loc[kochi_condition_exclude, 'Project key'] = 'noneed' # ================== 新增分析列 ================== # 1. ProjectNum dataframe['ProjectNum'] = dataframe['Project key'] # 2. BSW Self Test def is_bsw_self_test(row): if row['Involvement of BSW'] != 'Yes': return 0 found_by = row.get('Custom field (Found By)', '') labels = row.get('Labels', '') cond1 = found_by in ['Development Audit', 'Review', 'SW Unit Test', 'Development'] cond2 = (found_by == 'SW Integration Test') and ('BSW_IT' in labels) return 1 if cond1 or cond2 else 0 dataframe['BSW Self Test'] = dataframe.apply(is_bsw_self_test, axis=1) # 3. BSW Issue def is_bsw_issue(row): if not (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes'): return 0 custom_field = str(row.get('Custom field (Custom_2)', '')) keywords = ['平台问题', '开发设计问题', '三方包问题', 'Development Design Issue', 'Platform Issue', 'Party Package Issue'] return 1 if any(keyword in custom_field for keyword in keywords) else 0 dataframe['BSW Issue'] = dataframe.apply(is_bsw_issue, axis=1) # 4. Stack BSW Analyzed def is_stack_analyzed(row): if not (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes'): return 0 components = str(row.get('Component/s', '')) custom_field = str(row.get('Custom field (Custom_2)', '')) cond1 = 'Stack' in components cond2 = '非BSW问题' in custom_field or 'Non-BSW' in custom_field return 1 if cond1 or cond2 else 0 dataframe['Stack BSW Analyzed'] = dataframe.apply(is_stack_analyzed, axis=1) # 5. Stack BSW Unanalyzed def is_stack_unanalyzed(row): if not (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes'): return 0 components = str(row.get('Component/s', '')) custom_field = str(row.get('Custom field (Custom_2)', '')) cond1 = 'Stack' in components cond2 = '非BSW问题' in custom_field or 'Non-BSW' in custom_field return 1 if not (cond1 or cond2) else 0 dataframe['Stack BSW Unanalyzed'] = dataframe.apply(is_stack_unanalyzed, axis=1) # 6. Stack Total dataframe['Stack Total'] = dataframe.apply( lambda row: 1 if (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and 'Stack' in str(row.get('Component/s', ''))) else 0, axis=1 ) # 7. BSW Involve dataframe['BSW Involve'] = dataframe.apply( lambda row: 1 if (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes') else 0, axis=1 ) # 8. BSW Involve Unclosed dataframe['BSW Involve Unclosed'] = dataframe.apply( lambda row: 1 if (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes' and row.get('Status', '') != 'Closed') else 0, axis=1 ) # ------------------ HorizontalExpansion 分析列 ------------------ # 处理日期转换 if 'Created' in dataframe.columns: dataframe['Created'] = pd.to_datetime(dataframe['Created'], errors='coerce') days_since_creation = (current_time - dataframe['Created']).dt.days else: days_since_creation = pd.Series([0] * len(dataframe)) # 1. HorizontalExpansion Count dataframe['HorizontalExpansion Count'] = dataframe['HorizontalExpansion PN'].notna().astype(int) # 2. HorizontalExpansion Unfinished dataframe['HorizontalExpansion Unfinished'] = ( dataframe['HorizontalExpansion PN'].notna() & (dataframe['Status'] != 'Done') ).astype(int) # 3. HorizontalExpansion Delay dataframe['HorizontalExpansion Delay'] = ( dataframe['HorizontalExpansion PN'].notna() & (dataframe['Status'] != 'Done') & (days_since_creation > 30) ).astype(int) # ------------------ 原有业务逻辑列 ------------------ # BSW Analysis Conclusions def bsw_analysis_conclusions(row): if row['Involvement of BSW'] == 'No': return 'Non-BSW' elif row['Involvement of BSW'] == 'Yes': if pd.notna(row['Custom field (Custom_2)']): custom_field = row['Custom field (Custom_2)'] if '非BSW问题' in custom_field or 'Non-BSW' in custom_field: return 'Analyzed-Non-BSW' elif '开发设计问题' in custom_field or 'Development Design Issue' in custom_field: return 'Analyzed-BSW' elif '需求问题' in custom_field or 'Requirement Issue' in custom_field: return 'Analyzed-Non-BSW' elif '平台问题' in custom_field or 'Platform Issue' in custom_field: return 'Analyzed-BSW' elif '三方包问题' in custom_field or 'Party Package Issue' in custom_field: return 'Analyzed-三方包问题' return 'Analyzing-Unclassified' dataframe['BSW Analysis Conclusions'] = dataframe.apply(bsw_analysis_conclusions, axis=1) # Stack Classification def stack_classification(component): if pd.isna(component): return 'Unclassed' stacks = ['Com_Stack', 'Cdd_Stack', 'CybSec_Stack', 'Diag_Stack', 'Fbl_Stack', 'FuSa_Stack', 'IoHwAb_Stack', 'Mem_Stack', 'ModeMgm_Stack', 'OS_Stack', 'Sys_Stack'] for stack in stacks: if stack in component: return stack return 'Unclassed' dataframe['Stack Classification'] = dataframe['Component/s'].apply(stack_classification) # BSW Reason Classification def calculate_bsw_reason_classification(row): if row['Involvement of BSW'] == 'No': return '底层未参与分析' if pd.notna(row['Custom field (Custom_2)']): custom_field = row['Custom field (Custom_2)'] if '非BSW问题' in custom_field or 'Non-BSW' in custom_field: return '非BSW问题' elif '开发设计问题' in custom_field or 'Development Design Issue' in custom_field: return '开发设计问题' elif '需求问题' in custom_field or 'Requirement Issue' in custom_field: return '需求问题' elif '平台问题' in custom_field or 'Platform Issue' in custom_field: return '平台问题' elif '三方包问题' in custom_field or 'Party Package Issue' in custom_field: return '三方包问题' return 'Analyzing-Unclassified' dataframe['BSW Reason Classification'] = dataframe.apply(calculate_bsw_reason_classification, axis=1) # total 列 dataframe['total'] = dataframe.apply( lambda row: 0 if row['Project key'] == 'KOCHI_PL' else 1 if row['Project key'] != 'KOCHI_PL' and row['Issue Type'] in ["KOCHI AE Issue", "Problem"] else 0, axis=1 ) # BSW Analyzed # 修复 BSW Analyzed 列的计算错误 def calculate_bsw_analyzed(row): if not (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes'): return 0 custom_field = str(row.get('Custom field (Custom_2)', '')) keywords = ['需求问题', '平台问题', '开发设计问题', '非BSW问题', '三方包问题', 'Non-BSW', 'Development Design Issue', 'Requirement Issue', 'Platform Issue', 'Party Package Issue'] # 使用正则表达式检查关键词 pattern = r'需求问题|平台问题|开发设计问题|非BSW问题|三方包问题|Non-BSW|Development Design Issue|Requirement Issue|Platform Issue|Party Package Issue' return 1 if re.search(pattern, custom_field) else 0 dataframe['BSW Analyzed'] = dataframe.apply(calculate_bsw_analyzed, axis=1) # BSW Unanalyzed def calculate_bsw_unanalyzed(row): if not (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes'): return 0 custom_field = str(row.get('Custom field (Custom_2)', '')) keywords = ['需求问题', '平台问题', '开发设计问题', '非BSW问题', '三方包问题', 'Non-BSW', 'Development Design Issue', 'Requirement Issue', 'Platform Issue', 'Party Package Issue'] # 使用正则表达式检查关键词 pattern = r'需求问题|平台问题|开发设计问题|非BSW问题|三方包问题|Non-BSW|Development Design Issue|Requirement Issue|Platform Issue|Party Package Issue' return 1 if pd.isna(row['Custom field (Custom_2)']) or not re.search(pattern, custom_field) else 0 dataframe['BSW Unanalyzed'] = dataframe.apply(calculate_bsw_unanalyzed, axis=1) # BSW Staff List dataframe['BSW Staff List'] = dataframe['Assignee'].apply( lambda x: "Yes" if x in bsw_staff_set else "No" ) # Found By Classification def classify_found_by(row): found_by = row['Custom field (Found By)'] labels = row['Labels'] if pd.notna(row['Labels']) else "" if found_by == 'SW Integration Test' and 'BSW_IT' in labels: return 'BSW_IT' elif found_by == 'SW Integration Test': return 'SWIT' elif found_by == 'Cybersecurity Monitoring': return 'Cybersecurity Monitoring' elif found_by in ['HIL TEST', 'HILP TEST', 'System Test']: return 'System Test' elif found_by in ['OEM Test', 'OEM', 'End user']: return 'OEM&End user' elif found_by == 'Development Audit' and 'BSW_PT' in labels: return 'BSW_PT' elif found_by in ['Review', 'SW Unit Test', 'Development']: return '开发自测' return 'Others' dataframe['Found By Classification'] = dataframe.apply(classify_found_by, axis=1) # High Priority Unanalyzed def calculate_high_priority_unanalyzed(row): if not (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes' and row['Priority'] == 'High'): return 0 if pd.isna(row['Created']): return 0 days_diff = (current_time - row['Created']).days if days_diff <= 15: return 0 custom_field = str(row.get('Custom field (Custom_2)', '')) exclude_keywords = [ '需求问题', '平台问题', '开发设计问题', '非BSW问题', '三方包问题', 'Non-BSW', 'Development Design Issue', 'Requirement Issue', 'Platform Issue', 'Party Package Issue' ] if any(keyword in custom_field for keyword in exclude_keywords): return 0 return 1 dataframe['High Priority Unanalyzed'] = dataframe.apply(calculate_high_priority_unanalyzed, axis=1) # Stack_Analyzed dataframe['Stack_Analyzed'] = dataframe.apply( lambda row: 1 if (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes' and ('Stack' in str(row['Component/s']) or '非BSW问题' in str(row.get('Custom field (Custom_2)', '')) or 'Non-BSW' in str(row.get('Custom field (Custom_2)', '')))) else 0, axis=1 ) # Stack_Unanalyzed dataframe['Stack_Unanalyzed'] = dataframe.apply( lambda row: 1 if (row['Issue Type'] in ["KOCHI AE Issue", "Problem"] and row['Involvement of BSW'] == 'Yes' and not ('Stack' in str(row['Component/s']) or '非BSW问题' in str(row.get('Custom field (Custom_2)', '')) or 'Non-BSW' in str(row.get('Custom field (Custom_2)', '')))) else 0, axis=1 ) # 日期相关列 if 'Updated' in dataframe.columns: dataframe['Updated'] = pd.to_datetime(dataframe['Updated'], errors='coerce') days_since_update = (current_time - dataframe['Updated']).dt.days condition_base = ( (dataframe['Issue Type'].isin(["KOCHI AE Issue", "Problem"])) & (dataframe['Involvement of BSW'] == 'Yes') & (dataframe['BSW Staff List'] == 'Yes') ) dataframe['update_over_15'] = (condition_base & (days_since_update >= 15)).astype(int) dataframe['update_over_7'] = (condition_base & (days_since_update > 7) & (days_since_update < 15)).astype(int) logging.info("成功添加所有分析列") # 处理完成后调用进度回调 if progress_callback: progress_callback(total_rows, total_rows) return dataframe def assign_responsibles(dataframe, config_dict): """根据配置字典为 DataFrame 增加 'Responsible' 和 'Email' 列""" if not config_dict: logging.warning("配置字典为空,无法分配责任人") dataframe['Responsible'] = "Unassigned" dataframe['Email'] = "" return dataframe def get_responsible_and_email(project_key): # 处理无效项目键 project_key = str(project_key) # 首先检查具体项目配置 if project_key in config_dict: project_config = config_dict[project_key] return ( project_config.get("Responsible", "Unassigned"), project_config.get("Email", "") ) # 其次检查默认配置 default_config = config_dict.get("Default", {}) return ( default_config.get("Responsible", "Unassigned"), default_config.get("Email", "") ) dataframe['Responsible'], dataframe['Email'] = zip(*dataframe['Project key'].apply(get_responsible_and_email)) return dataframe # ------------------ 数据库相关 ------------------ def get_database_engine(db_name="default"): """创建数据库连接引擎""" prefix = "BSW_" if db_name == "bsw_jira" else "" host = os.getenv(f"{prefix}DB_HOST") user = os.getenv(f"{prefix}DB_USER") password = os.getenv(f"{prefix}DB_PASSWORD") database = os.getenv(f"{prefix}DB_NAME", db_name) port = os.getenv(f"{prefix}DB_PORT", 3306) if not all([host, user, password, database]): raise ValueError(f"缺少{db_name}数据库配置") connection_uri = f"mysql+pymysql://{user}:{password}@{host}:{port}/{database}" return create_engine(connection_uri) def write_to_database(dataframe, target_engine, target_table): """严格按目标表列名写入数据""" inspector = inspect(target_engine) if inspector.has_table(target_table): target_columns = [col['name'] for col in inspector.get_columns(target_table)] valid_columns = [col for col in dataframe.columns if col in target_columns] filtered_df = dataframe[valid_columns] skipped = set(dataframe.columns) - set(valid_columns) if skipped: logging.warning(f"跳过目标表不存在的列: {', '.join(skipped)}") else: filtered_df = dataframe logging.info(f"创建新表: {target_table}") filtered_df.to_sql( name=target_table, con=target_engine, index=False, if_exists='append', chunksize=500 ) return filtered_df def process_and_write_data(source_engine, target_engine, config_path): """查询、处理并写入数据(单次写入版本)""" try: source_table = os.getenv("SOURCE_TABLE_NAME") target_table = os.getenv("TARGET_TABLE_NAME") if not source_table or not target_table: raise ValueError("缺少环境变量 SOURCE_TABLE_NAME 或 TARGET_TABLE_NAME") logging.info(f"从 {source_table} 读取数据,写入 {target_table}") # 加载配置文件为字典 config_dict = load_config(config_path) if not config_dict: logging.error("配置加载失败,无法分配责任人") # 继续处理但跳过责任分配 bsw_staff_set = set() else: bsw_staff_set = set(config_dict.get("bsw_staff_list", [])) project_keys = ["P09135", "P09192", "P08499", "KOCHI_PL"] # 构建查询 query = f""" SELECT * FROM `{source_table}` WHERE `Project key` IN ({', '.join([f"'{key}'" for key in project_keys])}) """ # 一次性读取全部数据 df = pd.read_sql(query, con=source_engine) total_rows = len(df) if total_rows == 0: logging.info("未读取到数据,退出处理") return 0 logging.info(f"读取到 {total_rows} 条记录,开始处理...") # 添加列名存在性检查 - 关键修复点 required_columns = [ 'Issue Type', 'Involvement of BSW', 'Custom field (Custom_2)', 'Project key', 'Labels', 'Summary', 'Status', 'Component/s', 'Priority', 'Custom field (Found By)', 'Assignee' ] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: logging.warning(f"数据源缺少必要列: {', '.join(missing_columns)}") # 添加默认值避免后续错误 for col in missing_columns: df[col] = None # 进度计数器初始化 processed_count = 0 progress_percent = 0 # 进度回调函数 def progress_callback(current, total): nonlocal processed_count, progress_percent processed_count = current current_percent = int(current / total * 100) if current_percent > progress_percent: progress_percent = current_percent logging.info(f"处理进度: {current}/{total} ({progress_percent}%)") # 处理数据(添加进度回调) processed_df = add_columns(df, bsw_staff_set, progress_callback) processed_df = assign_responsibles(processed_df, config_dict) logging.info("数据处理完成,开始写入数据库...") # 单次写入数据库 filtered_df = write_to_database(processed_df, target_engine, target_table) actual_rows = len(filtered_df) logging.info(f"成功写入 {actual_rows} 条记录到 {target_table}") return actual_rows except Exception as e: logging.error(f"数据处理失败: {str(e)}") logging.error(traceback.format_exc()) return 0 # ------------------ 主函数 ------------------ if __name__ == "__main__": required_vars = ["SOURCE_TABLE_NAME", "TARGET_TABLE_NAME"] missing_vars = [var for var in required_vars if not os.getenv(var)] if missing_vars: logging.error(f"缺少环境变量: {', '.join(missing_vars)}") exit(1) os.makedirs(OUTPUT_DIR, exist_ok=True) # 创建双数据库连接 try: source_engine = get_database_engine("default") target_engine = get_database_engine("bsw_jira") except Exception as e: logging.error(f"数据库连接失败: {str(e)}") exit(1) # 处理并写入数据 try: row_count = process_and_write_data( source_engine=source_engine, target_engine=target_engine, config_path="responsible_config.json" ) logging.info(f"数据处理完成,共写入 {row_count} 条记录") exit(0) except Exception as e: logging.error(f"主流程执行失败: {str(e)}") exit(1)
11-26
课程设计报告:总体方案设计说明 一、软件开发环境配置 本系统采用C++作为核心编程语言,结合Qt 5.12.7框架进行图形用户界面开发。数据库管理系统选用MySQL,用于存储用户数据与小精灵信息。集成开发环境为Qt Creator,操作系统平台为Windows 10。 二、窗口界面架构设计 系统界面由多个功能模块构成,各模块职责明确,具体如下: 1. 起始界面模块(Widget) 作为应用程序的入口界面,提供初始导航功能。 2. 身份验证模块(Login) 负责处理用户登录与账户注册流程,实现身份认证机制。 3. 游戏主大厅模块(Lobby) 作为用户登录后的核心交互区域,集成各项功能入口。 4. 资源管理模块(BagWidget) 展示用户持有的全部小精灵资产,提供可视化资源管理界面。 5. 精灵详情模块(SpiritInfo) 呈现选定小精灵的完整属性数据与状态信息。 6. 用户名录模块(UserList) 系统内所有注册用户的基本信息列表展示界面。 7. 个人资料模块(UserInfo) 显示当前用户的详细账户资料与历史数据统计。 8. 服务器精灵选择模块(Choose) 对战准备阶段,从服务器可用精灵池中选取参战单位的专用界面。 9. 玩家精灵选择模块(Choose2) 对战准备阶段,从玩家自有精灵库中筛选参战单位的操作界面。 10. 对战演算模块(FightWidget) 实时模拟精灵对战过程,动态呈现战斗动画与状态变化。 11. 对战结算模块(ResultWidget) 对战结束后,系统生成并展示战斗结果报告与数据统计。 各模块通过统一的事件驱动机制实现数据通信与状态同步,确保系统功能的连贯性与数据一致性。界面布局遵循模块化设计原则,采用响应式视觉方案适配不同显示环境。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值