禅道Bug的一次迁移

news2025/2/28 12:49:17

一、场景

        平时工作记录在公司禅道上的问题想备份一份到本地,但是又没有公司禅道的数据库信息,有时候出测试报告想批量调整数据方便截图很困难,同时也为了学习禅道数据流转过程,所以有了把缺陷保存到本地一份的想法。

        实际上禅道支持导入缺陷,但是开源版不支持,所以就饶了一圈。

 二、思路:

        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 = None
    def 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_sqlData


class GenerateSql():
    def __init__(self):
        self.data = None

    def 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_statements
        else:
            sql_statements = []
            for id in file_id_list:
                sql_statement = sql_template.format(
                    id=id.replace("'", "''"),  # 转义单引号以防止SQL语法错误
                )
                sql_statements.append(sql_statement)
            return sql_statements

generate_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 io

def 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")  # 确保图片模式兼容JPEG
                file_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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux环境安装Jenkins

Linux环境安装Jenkins Jenkins和JDK的版本 Jenkins和JDK的版本需要对应,不然无法正常启动。 Jenkins稳定版下载地址 Jenkins服务 手动使用命令启动和关闭Jenkins比较麻烦,所以可以把Jenkins设置成开机启动。 创建Jenkins.sh文件 JAVA_HOME和jenk…

NLP论文速读(MetaMetrics)|使用人类偏好校准生成任务的度量

论文速读|METAMETRICS: CALIBRATING METRICS FOR GENERATION TASKS USING HUMAN PREFERENCES 论文信息: 简介: 本文探讨了在自然语言处理(NLP)和其他生成任务中,如何评估模型输出的质量以确保其与人类偏好一致。传统的…

【解决】k8s使用kubeadm初始化集群失败问题整理

执行提示命令,查看报错信息 journalctl -xeu kubelet1、错误:running with swap on is no 报错 "command failed" err"failed to run Kubelet: running with swap on is no 解决: swap未禁用,需要禁用swap&…

基于贝叶斯优化LightGBM模型对医院防火隐患区域火灾风险预测

一、引言 (一)研究背景与意义 医院作为人员密集、设备复杂且存放大量易燃易爆物品的场所,防火安全至关重要。一旦发生火灾,极易造成严重的人员伤亡和财产损失。火灾风险预测能够提前识别潜在的火灾隐患区域,为制定有…

第二届CN-fnst re题wp

题目附件名称amazingbruteforce 首先查壳,发现有upx壳(主页有教程,这边就不说了),脱掉壳,64位程序,拖进ida64进行反编译,如下图 很简单的逻辑,v5一个数组,v4…

Python系统教程008-条件判断(二)

知识回顾 1、if语句的基本语法? 2、常用 的比较运算符有哪些? 3、注释的分类以及格式 4、else处理条件不满足的情况 练习: 地板上有n个石子,猫头鹰和小兔子正在玩取石子的游戏,从猫头鹰开始,轮流取石子&a…

爬虫逆向学习(十四):分享一下某数通用破解服务开发经验

阅前须知 这篇博客不是教大家怎么实现的,而且告知大家有这个东西,或者说一种趋势,借此分享自己大致的实现经验。具体的实现我也不好整理,毕竟是在别人的基础上缝缝补补。 前言 使用补环境方式破解过某数的同学都知道&#xff0…

知识分享第二十八天-数学篇一

组合.二项式定理.常见导数 组合 让我们通过一个具体的例子来理解组合(Combinations)的概念 假设你有一个装有5个不同颜色球的袋子:红、蓝、绿、黄和紫。你想从中随机抽取3个球, 不考虑顺序,那么你可以有多少种不同的…

Kruskal最小生成树算法正确性证明

Kruskal: 每次考虑最短一条边, 如果会形成回路则不选择该边, 如果不会形成回路则选择该边, 直到选出了n-1条边 要点: 每次都选择不会形成回路的最短边 数学归纳法 ① n<2时, 显然成立 ② 设n k时成立, 则当n k1时: 将图划分为 子图A(k) 和 B(1) ∵ n k时成立 ∴ A(k)可…

Liveweb视频汇聚平台支持WebRTC协议赋能H.265视频流畅传输

随着科技的飞速发展和网络技术的不断革新&#xff0c;视频监控已经广泛应用于社会各个领域&#xff0c;成为现代安全管理的重要组成部分。在视频监控领域&#xff0c;视频编码技术的选择尤为重要&#xff0c;它不仅关系到视频的质量&#xff0c;还直接影响到视频的传输效率和兼…

iPhone苹果相册视频怎么提取音频?

在数字时代&#xff0c;视频已成为我们记录生活、分享故事的重要方式。然而&#xff0c;有时候我们只想保留视频中的音频部分&#xff0c;比如一段动人的背景音乐或是一段珍贵的对话。那么&#xff0c;苹果相册视频怎么提取音频呢&#xff1f;本文将介绍三种简单且实用的方法&a…

【图像配准】方法总结

图像配准(Image registration)就是将不同时间、不同传感器&#xff08;成像设备&#xff09;或不同条件下&#xff08;天候、照度、摄像位置和角度等&#xff09;获取的两幅或多幅图像进行匹配、叠加的过程&#xff0c;就是找到1幅图像像素到另1幅图像像素间的空间映射关系它已…

专业140+总分400+北京理工大学826信号处理导论考研经验北理工电子信息与通信工程,真题,大纲,参考书。

考研总分400&#xff0c;专业826信号处理导论&#xff08;信号与系统和dsp&#xff09;140&#xff0c;成功上岸北理工&#xff0c;虽然已经一段时间&#xff0c;但是后劲很大&#xff0c;每每回想还是昨日事&#xff0c;群里同学多次要求分享自己的一些经验&#xff0c;感谢大…

ReactPress最佳实践—搭建导航网站实战

Github项目地址&#xff1a;https://github.com/fecommunity/easy-blog 欢迎Star。 近期&#xff0c;阮一峰在科技爱好者周刊第 325 期中推荐了一款开源工具——ReactPress&#xff0c;ReactPress一个基于 Next.js 的博客和 CMS 系统&#xff0c;可查看 demo站点。&#xff08;…

Windows 环境实战开源项目GFPGAN 教程

GFPGAN GFPGAN&#xff08;Generative Facial Prior-GAN&#xff09;是由腾讯ARC&#xff08;Applied Research Center&#xff09;开发的一种实用的真实世界人脸修复算法。它专门设计用于人脸图像的生成和优化&#xff0c;尤其在低质量人脸图像的超分辨率恢复方面表现出色。以…

链表的应用

尾删 int tail_del (linkListPtr S) { if(NULL S || empty(S)) { printf("失败\n"); return 0; } linkListPtr q S; for(int i0 ; i<S->len-1;i) { qq->next; } free(q->next); q->n…

一个小工具

院内感染监控系统 开发工具Delphi7 报表工具FastReport unit U_Ymjg;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, RzButton, ExtCtrls, RzPanel, ImgList, Grids, RzGrids, StdCtrls,RzCmboBx, DB, ADODB;typeTFrm_ym…

Python:程序中如何引用环境变量

应用场景&#xff1a;具有安全性的信息&#xff08;如密钥&#xff09;&#xff0c;为方式代码提交被同时提交到公共环境&#xff08;如git&#xff09;&#xff0c;可以通过环境变量配置后&#xff0c;在代码中直接引用。 优点&#xff1a;安全、复用性强、持久化&#xff08…

电力场景绝缘子缺陷识别分割数据集labelme格式1099张3类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;1099 标注数量(json文件个数)&#xff1a;1099 标注类别数&#xff1a;3 标注类别名称:["brokenpart","brokeninsulator…

C++ -- 哈希表封装实现unordered_map 和 unordered_set

本章内容分为源码看框架讲解和结构模拟实现两部分&#xff0c;源码框架是让我们了解容器结构在设计时的思路&#xff0c;模拟实现才是重点。因此如果在看源码结构式感到疑惑&#xff0c;不妨继续往下看&#xff0c;相信一切都会慢慢了解~ 源码及框架分析 在C98 / SGI-STL30版本…