当前位置: 首页 > news >正文

禅道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 通过着色器如何实现全息影像&#xff0c;以及一些动态的效果。 一些难点的思维&#xff0c;代码目录 下面图是摄像机视角观看影响上的时候&#xff0c;如何实现光影的渐变&#xff0c;透视以及叠加等。 一、代码 1.index.html <!DOCTYPE html> <html …...

文件包含进阶玩法以及绕过姿态

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

Markdown编辑器工具--Typora

下载链接...

PyTorch 的 torch.unbind 函数详解与进阶应用:中英双语

中文版 PyTorch 的 torch.unbind 函数详解与进阶应用 在深度学习中&#xff0c;张量的维度操作是基础又重要的内容。PyTorch 提供了许多方便的工具来完成这些操作&#xff0c;其中之一便是 torch.unbind。与常见的堆叠函数&#xff08;如 torch.stack&#xff09;相辅相成&am…...

四十六:如何使用Wireshark解密TLS/SSL报文?

TLS/SSL是保护网络通信的重要协议&#xff0c;其加密机制可以有效地防止敏感信息被窃取。然而&#xff0c;在调试网络应用或分析安全问题时&#xff0c;解密TLS/SSL流量是不可避免的需求。本文将介绍如何使用Wireshark解密TLS/SSL报文。 前提条件 在解密TLS/SSL报文之前&…...

【人工智能】OpenAI O1模型:超越GPT-4的长上下文RAG性能详解与优化指南

在人工智能&#xff08;AI&#xff09;领域&#xff0c;长上下文生成与检索&#xff08;RAG&#xff09; 已成为提升自然语言处理&#xff08;NLP&#xff09;模型性能的关键技术之一。随着数据规模与应用场景的不断扩展&#xff0c;如何高效地处理海量上下文信息&#xff0c;成…...

Ubuntu22.04搭建FTP服务器保姆级教程

在网络环境中&#xff0c;文件传输是一项至关重要的任务。FTP&#xff08;文件传输协议&#xff09;是一种基于客户端/服务器模式的协议&#xff0c;广泛用于在互联网上传输文件。Ubuntu作为一款流行的Linux发行版&#xff0c;因其稳定性和易用性而广受开发者和系统管理员的喜爱…...

操作系统(4)操作系统的结构

一、无序结构&#xff08;整体结构或模块组合结构&#xff09; 1.特点&#xff1a; 以大型表格和队列为中心&#xff0c;操作系统的各部分程序围绕着这些表格进行。操作系统由许多标准的、可兼容的基本单位&#xff08;称为模块&#xff09;构成&#xff0c;模块之间通过规定的…...

Python数据分析(OpenCV视频处理)

处理视频我们引入的还是numpy 和 OpenCV 的包 引入方式如下&#xff1a; import numpy as np import cv2 我们使用OpenCV来加载本地视频&#xff0c;参数就是你视频的路径就可以 #加载视频 cap cv2.VideoCapture(./1.mp4) 下面我们进行读取视频 #读取视频 flag,frame cap.re…...

跨域 Cookie 共享

跨域请求经常遇到需要携带 cookie 的场景&#xff0c;为了确保跨域请求能够携带用户的认证信息或其他状态&#xff0c;浏览器提供了 withCredentials 这个属性。 如何在 Axios 中使用 withCredentials 为了在跨域请求中携带 cookie&#xff0c;需要在 Axios 配置中设置 withCr…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...