禅道Bug的一次迁移
一、场景
平时工作记录在公司禅道上的问题想备份一份到本地,但是又没有公司禅道的数据库信息,有时候出测试报告想批量调整数据方便截图很困难,同时也为了学习禅道数据流转过程,所以有了把缺陷保存到本地一份的想法。
实际上禅道支持导入缺陷,但是开源版不支持,所以就饶了一圈。
二、思路:
1.直接备份整个数据库行不通,考虑通过接口获取缺陷信息,生成sql插入到本地数据库中。
2.不使用接口获取信息,直接导出缺陷数据,通过获取Excel信息生成sql插入到本地数据库。
这里使用思路一,因为禅道官方提供了二次开发指南,包括基本的接口文档等等,主要是更方便学习以接口的方式操作禅道。
三、操作流程
1.查看禅道官方文档,获取token是第一步
获取Token - 禅道二次开发手册 - 禅道项目管理软件
2.根据提供的文档,确定请求地址和方式,调试后可获取token。注:url_base(前置URL)可以根据登录禅道进行获取。例如:http://192.168.1.1:8080/zentao/user-login-L21heDQxMi8=.html,这个地址就是http://192.168.1.1:8080/zentao
3.获取bug信息。注:在官方手册上只能找到获取产品bug的接口文档,但是我的禅道上没有产品这个概念,只有项目。实际上把products改为projects可以直接获取项目的bug信息,其接口文档的评论区上也提供了其他方法,但是不好用。这里加上status、limit两个参数可以查看所有的bug。
4.信息可以拿到,开始分析数据库结构,主要是步骤中图片文件的关联关系,体现在zt_file和zt_bug两张表。
对应关系1:zt_bug表中step字段文件名的前缀数字即对应zt_file文件的id
对应关系2:zt_file表文件pathname和图片路径的关联关系,比如2024/12/1.png对应的就是ZenTao/app/zentao/www/data/upload/1/202412/1.png。注:因为是插入的数据,所以pathname就用的id,实际上禅道会对创建产生的图片文件会进行加密,肯定不会叫这个名字
5.理清接口调用和数据库关联关系后,开始写代码。这里使用python进行操作。代码结构如下:其中获取sql等场景操作都在scene下,其余都是封装的一些方法。
5.1 获取sql所需要的数据信息
from api.zentao import zentao
import json
from common.json_extractor import extractor
from common.read_data import data
class Get_sqlData():def __init__(self):self.data = Nonedef get_sql_data(self,header,json_data):json_data = json.dumps(json_data)res = zentao.zentao_login(data=json_data, headers=header)data.update_ini("../config/setting.ini", "token", "token", res.json()['token'])print(res.json()['token'])header = {"Content-Type": "application/json","token": res.json()['token']}res = zentao.zentao_bug(id="5", headers=header)# print(res.json())title_list = extractor.extract_by_jsonpath(res.json(), "$.bugs[*].title")steps_list = extractor.extract_by_jsonpath(res.json(), "$.bugs[*].steps")# print(steps_list)pattern = r'\{(\d+)\.(?:jpeg|png)\}'extracted_numbers = extractor.extract_numbers_from_img_tags(steps_list, pattern)# print(extracted_numbers)return title_list,steps_list,extracted_numbers
get_sqlData = Get_sqlData()
if __name__ == '__main__':header = {"Content-Type": "application/json"}json_data = {"account" : "admin","password" : "123456"}res = get_sqlData.get_sql_data(header,json_data)print(res)
5.2 生成sql语句,main函数中注释的部分用于切换生成哪个sql语句,这里需要生成zt_file和zt_bug两个表的sql语句。
from get_sql_data import get_sqlDataclass GenerateSql():def __init__(self):self.data = Nonedef generate_sql_bug(self,sql_template,title_list=None,step_list=None,file_id_list=None):if file_id_list==None:sql_statements = []for title,step in zip(title_list,step_list):# 使用str.format()方法替换占位符sql_statement = sql_template.format(title=title.replace("'", "''"), # 转义单引号以防止SQL语法错误steps=step.replace("'", "''") # 同样转义HTML标签中的单引号)sql_statements.append(sql_statement)return sql_statementselse:sql_statements = []for id in file_id_list:sql_statement = sql_template.format(id=id.replace("'", "''"), # 转义单引号以防止SQL语法错误)sql_statements.append(sql_statement)return sql_statementsgenerate_sql = GenerateSql()
if __name__ == '__main__':sql_template = """INSERT INTO `zentao`.`zt_bug` (`product`, `title`, `severity`, `pri`, `type`,`steps`, `status`, `openedBy`, `openedDate`, `openedBuild`,`assignedTo`, `assignedDate`, `resolvedBy`, `resolution`,`resolvedBuild`, `resolvedDate`, `closedBy`, `closedDate`,`lastEditedBy`, `lastEditedDate`) VALUES (1, '{title}', 3, 3, 'codeerror','{steps}','closed', 'admin', '2024-12-09 17:06:17', 'trunk','admin', '2024-12-10 16:33:15','admin', 'fixed', 'trunk', '2024-12-10 00:00:00','admin', '2024-12-10 00:00:00','admin', '2024-12-10 16:33:15');"""sql_template_file = """INSERT INTO `zentao`.`zt_file` ( `id`, `pathname`, `title`, `extension`, `objectType`, `objectID`, `addedBy`, `addedDate`, `downloads`, `extra`, `deleted` )VALUES( {id}, '202412/{id}.png', 'image.png', 'png', '', 0, 'admin', '2024-12-10 00:00:00', 0, '', '0' );"""
# print(type(sql))header = {"Content-Type": "application/json"}data = {"account" : "admin","password" : "123456"}data_list = get_sqlData.get_sql_data(header,data)# sql = generate_sql.generate_sql_bug(sql_template,title_list=data_list[0],step_list=data_list[1])# for i in sql:# print(i)sql =generate_sql.generate_sql_bug(sql_template_file,file_id_list=data_list[2])for i in sql:print(i)
5.3 下载图片至本地禅道路径
from concurrent.futures import ThreadPoolExecutor, as_completed
from common.read_data import data
from get_sql_data import get_sqlData
import os
import requests
from urllib.parse import urlparse, unquote
from common.logger import logger
from PIL import Image
import iodef download_image(url, save_path, id):# 确保目录存在if not os.path.exists(save_path):os.makedirs(save_path)try:token = data.load_ini("../config/setting.ini")['token']['token']header = {"token": token}response = requests.get(url, headers=header, stream=True)if response.status_code == 200:try:img = Image.open(io.BytesIO(response.content))filename = f"{id}.png"img = img.convert("RGB") # 确保图片模式兼容JPEGfile_path = os.path.join(save_path, filename)img.save(file_path, 'JPEG')logger.info(f"Downloaded and saved as JPEG: {url} to {filename}")except Exception as e:logger.error(f"Error processing image from {url}: {e}")else:logger.error(f"Failed to download image {url}")with open(file_path, 'wb') as file:file.write(response.content)print(f"Downloaded: {filename}")except Exception as e:print(f"Failed to download {url}. Error: {e}")def download_images(url_list, save_path, id_list, max_workers=5):# 使用线程池并发下载图片with ThreadPoolExecutor(max_workers=max_workers) as executor:futures = [executor.submit(download_image, url, save_path, id) for url, id in zip(url_list, id_list)]for future in as_completed(futures):future.result() # 获取结果并处理异常(如果有的话)if __name__ == '__main__':header = {"Content-Type": "application/json"}json_data = {"account": "admin","password": "123456"}res = get_sqlData.get_sql_data(header, json_data)id = res[2]url_base = data.load_ini("../config/setting.ini")['host_Portal_System_Test']['api_root_url']url_list = [url_base + f"/file-read-{i}.png" for i in id]print(url_list)path = r"D:\ZenTao\app\zentao\www\data\upload\1\202412"# 调用函数下载图片download_images(url_list, path, id_list=id)
相关文章:

禅道Bug的一次迁移
一、场景 平时工作记录在公司禅道上的问题想备份一份到本地,但是又没有公司禅道的数据库信息,有时候出测试报告想批量调整数据方便截图很困难,同时也为了学习禅道数据流转过程,所以有了把缺陷保存到本地一份的想法。 实际上禅道支…...
c段和旁站讲解(附查询网址)
1. C段(C类子网段) C段就是一个IP地址的小范围。比如,假设你有一个家庭Wi-Fi网络,Wi-Fi会分配给你一组IP地址(每个设备一个IP地址)。如果你的网络分配的是类似 192.168.1.0 这样的IP地址,那么这…...
Linux编译Kernel时的文件zImage、文件dtb(dtbs)、核心模块分别是什么东西?
zImage文件的介绍 在编译Linux内核时,zImage 是一种内核映像文件,它是内核的压缩版本,通常用于引导嵌入式设备或其他资源有限的环境。 zImage 的具体含义 zImage 是 “Compressed Kernel Image” 的缩写。它是通过压缩原始的内核映像&…...
【深度学习】深刻理解“变形金刚”——Transformer
Transformer 是一种用于处理序列数据的深度学习模型架构,最初由 Vaswani 等人在 2017 年的论文《Attention is All You Need》中提出。它彻底改变了自然语言处理(NLP)领域,成为许多高级任务(如机器翻译、文本生成、问答…...
75_pandas.DataFrame 中查看和复制
75_pandas.DataFrame 中查看和复制 与pandas的DataFrame与NumPy数组ndarray类似,也有视图(view)和拷贝(copy)。 当使用loc[]或iloc[]等选择DataFrame的一部分以生成新的DataFrame时,与原对象共享内存的对…...

打电话玩手机识别-支持YOLO,COCO,VOC格式的标记,超高识别率可检测到手持打电话, 非接触式打电话,玩手机自拍等
打电话玩手机识别-支持YOLO,COCO,VOC格式的标记,超高识别率可检测到手持打电话, 非接触式打电话,玩手机自拍等1275个图片。 手持打电话: 非接触打电话 玩手机 数据集下载 yolov11:https://download.csdn…...

生产慎用之调试日志对空间矢量数据批量插入的性能影响-以MybatisPlus为例
目录 前言 一、一些缘由 1、性能分析 二、插入方式调整 1、批量插入的实现 2、MP的批量插入实现 3、日志的配置 三、默认处理方式 1、基础程序代码 2、执行情况 四、提升调试日志等级 1、在logback中进行设置 2、提升后的效果 五、总结 前言 在现代软件开发中,性能优…...
单片机:实现倒计时(附带源码)
使用单片机实现倒计时功能是一个常见的嵌入式应用,它能帮助你更好地理解如何进行时间控制和如何通过定时器实现精确的倒计时。通过该项目,你将学习如何使用单片机的定时器来进行时间计算,并通过LED或LCD显示倒计时的结果。 1. 项目概述 倒计…...
什么是多线程中的上下文切换
什么是多线程中的上下文切换 回答 上下文切换是指CPU从一个线程转到另一个线程时,需要保存当前线程的上下文状态,恢复另一个线程的上下文状态,以便于下一次恢复执行该线程时能够正确地运行。 在多线程编程中,上下文切换是一种常…...
如何在windwos批量拉取go mod
golang go-zero微服务开发,分的rpc项目太多了,变更了公共包,需要手动去拉取,直接一键拉取就好了,创建一个windwos脚本文件 文件名 tidy_all_go_mod.ps1 代码 # 辅助工具拉取go mod tidy # 根目录v99main执行 ./tidy_all_go_mod.ps1 # 定义项目的根目录 $RootDir Get-Locat…...

【Three.js基础学习】29.Hologram Shader
前言 three.js 通过着色器如何实现全息影像,以及一些动态的效果。 一些难点的思维,代码目录 下面图是摄像机视角观看影响上的时候,如何实现光影的渐变,透视以及叠加等。 一、代码 1.index.html <!DOCTYPE html> <html …...

文件包含进阶玩法以及绕过姿态
前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理文件包含漏洞的进阶玩法与绕过姿态 不涉及基础原理了 特殊玩法汇总 本地包含 文件包含上传文件 原理: php的文件包含有着把其他文件类型当做php代码执行的功效,文件上传一般会限制后缀&am…...

Markdown编辑器工具--Typora
下载链接...
PyTorch 的 torch.unbind 函数详解与进阶应用:中英双语
中文版 PyTorch 的 torch.unbind 函数详解与进阶应用 在深度学习中,张量的维度操作是基础又重要的内容。PyTorch 提供了许多方便的工具来完成这些操作,其中之一便是 torch.unbind。与常见的堆叠函数(如 torch.stack)相辅相成&am…...
四十六:如何使用Wireshark解密TLS/SSL报文?
TLS/SSL是保护网络通信的重要协议,其加密机制可以有效地防止敏感信息被窃取。然而,在调试网络应用或分析安全问题时,解密TLS/SSL流量是不可避免的需求。本文将介绍如何使用Wireshark解密TLS/SSL报文。 前提条件 在解密TLS/SSL报文之前&…...

【人工智能】OpenAI O1模型:超越GPT-4的长上下文RAG性能详解与优化指南
在人工智能(AI)领域,长上下文生成与检索(RAG) 已成为提升自然语言处理(NLP)模型性能的关键技术之一。随着数据规模与应用场景的不断扩展,如何高效地处理海量上下文信息,成…...

Ubuntu22.04搭建FTP服务器保姆级教程
在网络环境中,文件传输是一项至关重要的任务。FTP(文件传输协议)是一种基于客户端/服务器模式的协议,广泛用于在互联网上传输文件。Ubuntu作为一款流行的Linux发行版,因其稳定性和易用性而广受开发者和系统管理员的喜爱…...

操作系统(4)操作系统的结构
一、无序结构(整体结构或模块组合结构) 1.特点: 以大型表格和队列为中心,操作系统的各部分程序围绕着这些表格进行。操作系统由许多标准的、可兼容的基本单位(称为模块)构成,模块之间通过规定的…...
Python数据分析(OpenCV视频处理)
处理视频我们引入的还是numpy 和 OpenCV 的包 引入方式如下: import numpy as np import cv2 我们使用OpenCV来加载本地视频,参数就是你视频的路径就可以 #加载视频 cap cv2.VideoCapture(./1.mp4) 下面我们进行读取视频 #读取视频 flag,frame cap.re…...
跨域 Cookie 共享
跨域请求经常遇到需要携带 cookie 的场景,为了确保跨域请求能够携带用户的认证信息或其他状态,浏览器提供了 withCredentials 这个属性。 如何在 Axios 中使用 withCredentials 为了在跨域请求中携带 cookie,需要在 Axios 配置中设置 withCr…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...