最新要闻
- 天天即时:《封神三部曲》首部即将上映:制作成本或为16.5亿元
- 如何拍出女朋友最美一面?OPPO Reno10 Pro+评测:长焦人像让直男也会拍照
- 今日关注:不到1斤重!宏碁蜂鸟迷你主机开卖:1499元起
- 安全带又出现问题!韩系豪华品牌捷尼赛思宣布召回G80、GV70等共计1602辆|环球最新
- 比亚迪西安工厂起火 现场浓烟滚滚!官方回应
- 当前短讯!什么可以防电脑辐射_什么防辐射
- 全球短讯!小鹏“掉队”,它究竟做错了什么?
- 单日票房占比超50%!《蜘蛛侠:纵横宇宙》评分解禁:9.0超高分
- 又一“巨无霸”!国产2万吨重载自动驾驶列车试验成功
- 2.9秒下完一部电影!Wi-Fi 7要来了:国内标准已落地
- moto razr 40 ultra为何要装一块超大尺寸外屏?看完明白了
- 中国最深高铁站感受下:仅扶梯垂直高度42米 相当于15层楼 每日动态
- 在西部脱颖而出的丹佛掘金确实是被所有人低估了-今日最新
- 美国登月被指造假 50多年前的通信技术做不到?大V科普|世界播资讯
- Win11新画图推出两年后:承诺的深色模式终于有了|环球速读
- 别执迷不悟!张朝阳:上名牌和一般大学区别不太大 对你一生影响不大
广告
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
利用jira及confluence的API进行批量操作(查找/更新/导出/备份/删除等)
(相关资料图)
前言:
近期因为某些原因需要批量替换掉 jira 和 confluence中的特定关键字,而且在替换前还希望进行备份(以便后续恢复)和导出(方便查看)atlassian官方的api介绍文档太简陋,很多传参都没有进一步的描述说明,过程中踩了不少的坑...故现将相关代码分享下,希望有类似需求的朋友能用得上,直接上代码:
from jira import JIRAimport requestsimport re"""用途: jira单的查找、导出、更新、删除等操作author: tonydate: 2023"""class jira_tools(): # jira API base_url = "http://your-jira-url.com/" username = "your_username" password = "your_password" jira = JIRA(base_url,basic_auth=(username, password)) # 搜索关键字和替换关键字 search_keyword = "查找关键词" replace_keyword = "替换关键词" def jira_search(self): """查找标题和正文中包含特定关键字的issue 返回一个list,list中的元素为jira issue对象 """ # 拼接jql,可按需修改(此处为搜索项目REQ和TREQ中的标题or描述中包含特定关键词的issue) jql_query = "project in (REQ,TREQ) AND (summary ~ "{0}" or description ~ "{0}") ORDER BY updated DESC".format(self.search_keyword) # jql_query = "summary ~ "{0}" or description ~ "{0}" ORDER BY updated DESC".format(self.search_keyword) # jql_query = "id = BUG-44257" # 每页的大小(应该最大只支持50) page_size = 50 # 初始化起始索引和总体issues列表 start_at = 0 all_issues = [] while True: # 执行查询并获取当前页的问题 issues = self.jira.search_issues(jql_query, startAt=start_at, maxResults=page_size) # 将当前页的issues添加到总体issues列表 all_issues.extend(issues) # 检查是否已获取所有issues if len(issues) < page_size: break # 更新起始索引以获取下一页 start_at += page_size return all_issues def jira_export(self, issue_id, issue_summary): # 页面上抓到的导出接口(需要先行在浏览器上登录) export_url = "http://your-jira-url.com/si/jira.issueviews:issue-word/{0}/{0}.doc".format(issue_id) #替换掉标题中可能存在的特殊关键字,避免保存文件失败 issue_summary = re.sub(r"[【】|()()\\/::<>*]", "", issue_summary) filename = "D:/jira_bak/{0}_{1}.doc".format(issue_id, issue_summary) # 下载后保存的文件名 response = requests.get(export_url) if response.status_code == 200: try: with open(filename, "wb") as f: f.write(response.content) print("issue导出成功!") except Exception as e: print("issue导出失败~失败原因:{0}".format(e)) def jira_replace(self,issues): """替换issue标题和正文中的特定关键字""" for issue in issues: issue_id = issue.key issue_obj = self.jira.issue(issue_id) # 获取原始标题和描述 old_summary = issue_obj.fields.summary old_description = issue_obj.fields.description # 先导出word self.jira_export(issue_id, old_summary) # 替换关键字 new_summary = old_summary.replace(self.search_keyword, self.replace_keyword) # 更新问题的标题和描述(description) if old_description: # 描述可能为空 new_description = old_description.replace(self.search_keyword, self.replace_keyword) issue_obj.update(summary=new_summary, description=new_description) else: issue_obj.update(summary=new_summary) # 更新问题的标题和描述 print("{0}-{1} 关键词替换成功".format(issue_id, old_summary)) def jira_delete(self, issue_id): """删除特定的issue""" try: # 获取issue issue = self.jira.issue(issue_id) # 删除issue issue.delete() print("{0}删除成功".format(issue_id)) except Exception as e: print("{0}删除失败:{1}".format(issue_id, e))# # 查找、备份/替换# j = jira_tools()# issues = j.jira_search()# issues_id_list = [ issue.key for issue in issues]# print(len(issues_id_list),issues_id_list)# j.jira_replace(issues)# 删除# j=jira_tools()# j.jira_delete("TREQ-18431")
import requestsimport re,osimport pandas as pdfrom atlassian import Confluence # pip install atlassian-python-api"""用途: confluence的查找、备份/导出、更新、删除、恢复等相关操作author: tonydate: 2023"""def save_content_to_file(filename, content, file_format="txt"): """保存内容到文件""" if file_format=="pdf": directory = "D:/wiki_bak/pdf/" filename = directory + filename + ".pdf" else: directory = "D:/wiki_bak/txt/" filename = directory + filename + ".txt" try: os.makedirs(directory, exist_ok=True) with open(filename, "wb" if file_format == "pdf" else "w", encoding="utf-8" if file_format != "pdf" else None) as file: file.write(content) print("内容已保存到文件{0}".format(filename)) except Exception as e: print("{0} 文档保存时失败:{1}".format(filename, e))class wiki_tools(): # Confluence API base_url = "http://your-confluence-url.com/" search_url = base_url + "/rest/api/search" content_url = base_url + "/rest/api/content" username = "your_username" password = "your_password" wiki_replace_record = "D:/wiki_bak/wiki_replace_record.csv" #处理过的文档概况 # 搜索关键字和替换关键字 search_keyword = ""查找关键词"" # 将搜索词用""号扩起来表示进行整词匹配,不会被confluence拆分成多个单词进行匹配 replace_keyword = "替换关键词" def wiki_search(self): """查找confluence文档 查找关键词: search_keyword returns: list:匹配文档的content_id(即URL上的pageId) """ content_id_list = [] # 用于记录文档id start = 0 limit = 100 total_size = 0 while start <= total_size: # 构建搜索请求的URL search_url = "{0}?cql=type=page and (title~"{1}" OR text~"{2}")&start={3}&limit={4}".format( self.search_url, self.search_keyword, self.search_keyword, start, limit) # 发送搜索请求 response = requests.get(search_url, auth=(self.username, self.password)) search_results = response.json() total_size = search_results["totalSize"] # 提取当前页匹配的文档 id page_content_id_list = [ result["content"]["id"] for result in search_results["results"]] content_id_list.extend(page_content_id_list) start += limit return content_id_list def wiki_replace(self,content_id): """替换confluence文档中的关键字""" # 获取文档正文部分内容 # https://community.atlassian.com/t5/Confluence-questions/How-to-edit-the-page-content-using-rest-api/qaq-p/904345 content_url = self.content_url + "/" + content_id + "?expand=body.storage,version,history" content_response = requests.get(content_url, auth=(self.username, self.password)) if content_response.status_code == 200: content_data = content_response.json() # 获取文档最新的版本号 latest_version = content_data["version"]["number"] # 获取文档的创建者 createdBy = content_data["history"]["createdBy"]["displayName"] # 获取文档的创建时间 eg: 2023-05-30T11:02:44.000+08:00 createdDate = content_data["history"]["createdDate"].split("T")[0] # 获取文档的标题 old_title = content_data["title"] # 替换掉标题中的特殊字符,避免无法作为文件命名 old_title = re.sub(r"[【】|()()\\/::<>*]", "", old_title) # 获取文档的正文 old_body = content_data["body"]["storage"]["value"] # 保存文档标题和正文内容(文件名称: contentid_title, 文件内容: body),以便后续恢复 save_content_to_file(content_id + "_" + old_title, old_body) # 记录所有处理过的文档概要信息到csv文件(mode="a"即追加模式写入) pd.DataFrame(data=[[content_id, old_title, createdBy, createdDate]]).to_csv(self.wiki_replace_record, encoding="utf-8", index=None, mode="a", header=None) # 导出文档内容为pdf(方便直接查看) try: self.wiki_export_pdf(content_id, old_title + "_" + createdBy + "_" + createdDate) except Exception as e: # 有些文档较大可能会超时 print("{0}文档导出时发生异常:{1}".format(content_id, e)) # 避免出现无效更新造成version无谓增加 if self.search_keyword in old_title or self.search_keyword in old_body: # 替换文档标题和正文中的关键字 new_title = old_title.replace(self.search_keyword, self.replace_keyword) new_body = old_body.replace(self.search_keyword, self.replace_keyword) # 更新文档 update_data = { "title": new_title, "type": content_data["type"], "version":{ "number": latest_version + 1 # 使用最新版本号加1 }, "body": { "storage": { "value": new_body, "representation": "storage" } } } update_response = requests.put(content_url, auth=(self.username, self.password), json=update_data) if update_response.status_code == 200: print("替换成功:", old_title) else: print("替换失败:", old_title) else: print("文档中未包含关键字:{0},无需更新".format(self.search_keyword)) def wiki_update_from_file(self, content_id, title, body): """指定内容更新""" content_url = self.content_url + "/" + content_id + "?expand=body.storage,version" content_response = requests.get(content_url, auth=(self.username, self.password)) if content_response.status_code == 200: content_data = content_response.json() # 获取文档最新的版本号 latest_version = content_data["version"]["number"] # 更新文档 update_data = { "title": title, "type": content_data["type"], "version":{ "number": latest_version + 1 # 使用最新版本号加1 }, "body": { "storage": { "value": body, "representation": "storage" } } } update_response = requests.put(content_url, auth=(self.username, self.password), json=update_data) if update_response.status_code == 200: print("恢复成功:", title) else: print("恢复失败:", title) def wiki_restore(self, path="D:/wiki_bak/txt/"): """根据备份的body文件恢复对应的confluence文档""" # 获取指定路径下的所有文件 files = os.listdir(path) for file_name in files: # 根据文件名解析content_id、标题 ( 形如: contentid_title.txt ) content_id = file_name.split("_")[0] title = file_name.split("_")[1].replace(".txt","") file_path = os.path.join(path, file_name) # 读取备份文件并恢复 if os.path.isfile(file_path): print("开始处理",file_path) with open(file_path, "r") as file: content = file.read() self.wiki_update_from_file(content_id, title, content) def wiki_export_pdf(self, content_id, filename): """利用atlassian-python-api库导出pdf""" confluence = Confluence( url=self.base_url, username=self.username, password=self.password) page = confluence.get_page_by_id(page_id=content_id) response = confluence.get_page_as_pdf(page["id"]) save_content_to_file(filename, content=response, file_format="pdf") def wiki_delete(self,content_id): """利用atlassian-python-api库删除特定文档""" confluence = Confluence( url=self.base_url, username=self.username, password=self.password) try: confluence.remove_content(content_id) print("文档 {0} 删除成功".format(content_id)) except Exception as e: print("文档 {0} 删除失败: {1}".format(content_id, e))# w = wiki_tools()# # 批量查询&替换wiki文档,同时备份替换前的内容# contentid_list = w.wiki_search()# print(contentid_list)# for i in contentid_list:# print("----开始处理:{0}----".format(i))# w.wiki_replace(i)# # 根据备份的文件恢复wiki文档内容# w.wiki_restore()# # 删除特定的文档 # w.wiki_delete("137295690")
关键词:
-
利用jira及confluence的API进行批量操作(查找/更新/导出/备份/删除等)
前言:近期因为某些原因需要批量替换掉jira和confluence中的特定关键字,而且在替换前还希望进行备份(以便
来源: -
Angular Google Charts教程_编程入门自学教程_菜鸟教程-免费教程分享 全球即时看
教程简介GoogleCharts是一个纯粹的基于JavaScript的图表库,旨在通过添加交互式图表功能来增强Web应用程
来源: 利用jira及confluence的API进行批量操作(查找/更新/导出/备份/删除等)
Angular Google Charts教程_编程入门自学教程_菜鸟教程-免费教程分享 全球即时看
【天天新视野】财政系统组织申报2023年第二批专项债项目:13领域可用专项债作资本金
看点:商品日报(6月2日):市场风险偏好继续改善 玻璃涨停沪镍纯碱涨超4%
天天即时:《封神三部曲》首部即将上映:制作成本或为16.5亿元
如何拍出女朋友最美一面?OPPO Reno10 Pro+评测:长焦人像让直男也会拍照
今日关注:不到1斤重!宏碁蜂鸟迷你主机开卖:1499元起
安全带又出现问题!韩系豪华品牌捷尼赛思宣布召回G80、GV70等共计1602辆|环球最新
比亚迪西安工厂起火 现场浓烟滚滚!官方回应
当前短讯!什么可以防电脑辐射_什么防辐射
又双叒叕种草了新家装风格?AI帮你家居换装-全球今头条
vul -- Cybero靶机渗透
各大建站方案
今日快看!【解决方法】锐捷路由器配置IP地址,如RSR路由器
失业率下降影响市场甚微 短端日债延续走强
全球短讯!小鹏“掉队”,它究竟做错了什么?
单日票房占比超50%!《蜘蛛侠:纵横宇宙》评分解禁:9.0超高分
又一“巨无霸”!国产2万吨重载自动驾驶列车试验成功
2.9秒下完一部电影!Wi-Fi 7要来了:国内标准已落地
moto razr 40 ultra为何要装一块超大尺寸外屏?看完明白了
中国最深高铁站感受下:仅扶梯垂直高度42米 相当于15层楼 每日动态
2023.06 微信抓包方案 · 亲测可用
ASP.Net Core 刷新界面,内存持续增加不释放问题。
世界焦点!数据展示新利器:雷达图的魅力与优势
在西部脱颖而出的丹佛掘金确实是被所有人低估了-今日最新
美国登月被指造假 50多年前的通信技术做不到?大V科普|世界播资讯
Win11新画图推出两年后:承诺的深色模式终于有了|环球速读
别执迷不悟!张朝阳:上名牌和一般大学区别不太大 对你一生影响不大
iPhone 15 Pro系列屏幕供应商敲定:有三星和LG两家 全球今热点
长沙两摩托车笼中表演时相撞 惊险一幕曝光
焦点速递!H&M关店启示录
nginx在代理到upstream时转换http1.1为http1.0,长连接转为短连接
windows10 查看已连接wifi的密码
执行计划缓存,Prepared Statement性能跃升的秘密_天天速讯
专访:美国操纵G7干预别国事务——访巴西圣保罗州立大学教授保利诺 环球观点
环球看热讯:Apple Watch表带为何那么贵?原因揭开
理想“L6” 被长城给抢先造出来了:仅售23万!
天天新动态:小米汽车多项高层职务调整:前麦格纳高管黄振宇接管汽车供应链
全球速看:百度网盘内测大模型版云一朵智能助理:翻译、找图一句话搞定
599元 联想小新24 FHD高刷屏上架:100Hz刷新率 硬件级护眼_世界快资讯
一文读懂责任分配矩阵,解决你80%的项目难题 播报
今年618最值得买的手机:华为Pocket S限时优惠1000元、12期分期免息 天天即时
西游记主题公园游客吵架“牛魔王”劝架 幸运没人受伤:网友吐槽素质不如妖怪
蔷薇适合种在庭院什么地方?庭院种蔷薇如何养护?
四种立即改善在线安全的方法
国际清算银行:必须加强监管以防止更多银行倒闭|当前报道
热文:幼儿园摆20桌请200孩子吃席庆六一:每桌有龙虾、烧鸡、牛肉
还因续航焦虑不敢买电车吗?极氪001千里续航版交付:纯电1032km-世界动态
原价300元:星巴克家享黑咖啡10包25元大促(不到1折)-环球看点
天天即时:捷豹路虎发布全新品牌Logo!路虎车标从此成“遗产”
学生网购小石头收到7斤巨石 画面很荒谬很搞笑:网友笑谈赚了 当前信息
天天微资讯!OPEN AI接入MidJourney 画图支持GPT4中文智能优化效果惊艳
易基因:DNA羟甲基化和TET酶在胎盘发育和妊娠结局中的作用 | 深度综述_全球快资讯
环球焦点!规则引擎easyRules中组合规则的使用
windows10安装Sinpaste失败的解决
每日报道:社交app源码开发平台基础知识,软件二维码的生成
每日精选:香港特区政府发售近60亿美元绿色债券 增设10年期人民币债券
每日讯息!光华股份:公司电子封装材料用聚酯树脂目前处于小批量试产阶段,进展良好,已形成少量销售
理想车主跑高速 竟让婴儿平躺在副驾驶 网友直呼心真大-世界快资讯
国产性能车天花板!领克03高性能版首发:2.0T榨出350马力 今头条
全球实时:卢伟冰:Redmi K60在2000-3000档无敌 销量甩开友商
给孩子买豪车就差2个亿怎么办?用电锯做一辆!
尼康Z8禁用非原装电池 理由是出于安全考虑 用户:吃相难看
合纵科技:6月1日融资买入289.18万元,融资融券余额1.34亿元
世界即时看!windows10环境下安装RabbitMQ以及延时插件(图文)
RCEP对15个签署国全面生效
24 万辆 比亚迪 5 月赢麻了|今日热门
2999元 创维新款Mini LED显示器上架:4K HDR1000、96W反向充_世界热讯
世界快报:ChatGPT现在正式交到了一名华人手上
世界今亮点!不只比5G好用10倍 华为提出5.5G新战略:把光技术带入每一辆车
首发骁龙6 Gen 1!荣耀X50入网:1.5K OLED双曲屏
男子开咖啡店日入0元:之前一直在打工 突然想创业|滚动
Vue-自定义icon实现_每日快报
甘肃省发行新增专项债券416亿元 有力支持662个项目建设
50项精彩活动让孩子们乐享“六一” 世界新消息
要去月球抢水资源 NASA称美国将率先登月:4名宇航员停留300天|环球热闻
天网预演
12V小电瓶可能把车点着 福特召回超14万辆林肯SUV
全球即时看!16TB机械硬盘跌破千元 国内电商秒杀亚马逊 这价格还海淘?
焦点快看:联想GeekPro G5000锐龙本首发:RTX 4060秒杀价6499元
【世界报资讯】洋子聊球_洋子
巨石强森重拾速度与激情
世界快资讯丨重返世界首富 马斯克已坐私人飞机回国 发推大赞上海工厂及员工
年利率4.15%!苹果版“余额宝”上线 用户吐槽账户钱没了:官方回应
谁来拯救岌岌可危的油价?沙特、俄罗斯减产都带不动 欧美乐见局面|天天通讯
女子为拍照被卷入海中不幸身亡 只为打卡网红地:网友唏嘘不值得
威尔森_关于威尔森的介绍|每日播报
前5个月交易所债市融资超2万亿元 优质主体发行份额将进一步提升
显卡买RTX 4060Ti还是买RTX 3070?
林志颖即将复出 网友喊话:安全最重要!
环球头条:苹果计划扩大中国和印度市场:要在温州、佛山、合肥开新店
5G是高铁 6G就是飞机!中国移动:6G天地一体 感受爽到家|每日视点
儒林外史第六回概括300字(儒林外史第六回概括)
通讯!读改变未来的九大算法笔记01_数据压缩
拉普兰德
8GB显卡杀到1500元档 英特尔发布Arc新驱动:性价比无敌
618直降1000元 小米迷你主机2999元:0.4L体积 媲美Mac Mini 世界新动态
为什么大家爱买彩票了?国内彩票销售创新高 都想中大奖节奏
热讯:哥几个走着人物结局详细版介绍_哥几个走着真正结局
千万别让NVIDIA知道!有厂商偷偷做了RTX 3070 16GB 环球新视野