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

利用pandas gradio实现简单的项目子项拆解及排期

需求说明

客户有一堆项目计划内容和计划完成时间(d)、子项及延期时间的数据,请通过上述数据,生成一个项目排期计划。Excel数据示例见下,每个Sheet对应一个项目:

准备实验器材 7d 

验证器材完整性 3d 

生成测试报告 7d 技术报告 3-5d|测试用例5-7d

实现思路

借助gradio框架实现简单的界面交互,通过pandas计算项目的工期,通过内置的统计分析方法生成排期的开始和结束时间,这里通过固化当年的节日和周末,设置智能跳过。

完整代码

import gradio as gr
import pandas as pd
import re
import tempfile
from datetime import date
# 定义法定节假日列表(示例)
public_holidays = ['2025-01-01', '2025-01-02', '2025-04-04', '2025-05-01','2025-07-11','2025-07-14'# 添加更多法定节假日...
]
current_date = date.today()
formatted_date = current_date.strftime("%Y-%m-%d")def parse_and_sum(input_string):# 正则表达式匹配模式,用于找到形如 "数字-数字d" 的子串pattern = r'(\d+)-(\d+)d'# 查找所有匹配项matches = re.findall(pattern, input_string)# 初始化总和total_sum = 0# 遍历所有匹配项for match in matches:# 获取范围的最大值并累加到总和upper_bound = int(match[1])total_sum += upper_boundreturn total_sum
def load_excel(file,text_input="",sheet_name='Sheet1'):try:# 使用pandas读取上传的Excel文件df = pd.read_excel(file.name, sheet_name=sheet_name)if not text_input.strip():  #如果text_input为空,则使用当前日期text_input=formatted_date# 将DataFrame转换为列表##data_list = df.values.tolist()df2 = df2res(excelclean(df),text_input)##data_list = df2.values.tolist()#print(text_input)temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")excel_file_path = temp_file.namedf2.to_excel(excel_file_path, index=False)return excel_file_path#return data_listexcept Exception as e:return f"Error: {str(e)}"#该函数只考虑过滤周末,但未考虑节假日(废弃)
def update_value(current_time,span):current_time = pd.Timestamp(current_time)# 生成未来一段时间的日期范围,包含周末date_range = pd.date_range(start=current_time, periods=150, freq='D')  # 假设最多14天足够覆盖7个工作日# 筛选出非周六和周日的日期workdays = date_range[date_range.dayofweek < 5]  # dayofweek: Monday=0, Sunday=6# 取前7个工作日start_time = current_timeend_time = workdays[span].normalize()start_date_str = current_time.strftime('%Y-%m-%d')end_date_str = end_time.strftime('%Y-%m-%d')return  end_date_strdef business_days_diff(start_date, end_date):"""计算两个日期间的工作日天数差,跳过周末和法定节假日。参数:start_date (str): 开始日期,格式为 '年-月-日'。end_date (str): 结束日期,格式为 '年-月-日'。返回:int: 两个日期间的工作日天数差。"""# 将输入的字符串转换为pandas Timestamp对象start = pd.Timestamp(start_date)end = pd.Timestamp(end_date)# 转换节假日列表为Timestamp对象holidays = pd.to_datetime(public_holidays).tolist()# 创建一个CustomBusinessDay对象,传入节假日列表custom_bday = pd.offsets.CustomBusinessDay(weekmask='1111100', holidays=holidays)# 使用pd.date_range生成工作日序列business_days = pd.date_range(start=start, end=end, freq=custom_bday)# 返回工作日的数量return len(business_days) - 1  # 减去1是因为我们需要排除开始日期本身def get_sheet_names(file):try:# 获取Excel文件中的所有工作表名称xl = pd.ExcelFile(file.name)return xl.sheet_namesexcept Exception as e:return [f"Error: {str(e)}"]#计算
def calculate_end_date(start_date_str, span):"""根据起始日期和时间跨度计算结束日期,跳过周末和法定节假日。参数:start_date_str (str): 起始日期字符串,格式为'年-月-日'。span (int): 时间跨度,即需要跳过的天数。返回:str: 结束日期字符串,格式为'年-月-日'。"""# 将起始日期转换为pandas Timestamp对象current_date = pd.Timestamp(start_date_str)# 初始化计数器count = 0while count < span:# 向前移动一天current_date += pd.Timedelta(days=1)# 如果当前日期不是周末也不是法定假日,则计数器加一if current_date.dayofweek not in [5, 6] and current_date.strftime('%Y-%m-%d') not in public_holidays:count += 1return current_date.strftime('%Y-%m-%d')#Step1:读取Excel文件,清洗数据,返回Dataframe
def excelclean(df):df.columns = ["seq", "active", "days", "adjust"]new_df = pd.DataFrame(columns=['active', 'days', 'adjust', 'adj_num'])for index, row in df.iterrows():adj_num=row['days']if pd.notna(row['adjust']):adj_num=row['days']+parse_and_sum(row['adjust'])##print(f"{row['active']}\t{row['days']}\t{row['adjust']}\t{adj_num}")new_row = {'active': row['active'],'days': row['days'],'adjust': row['adjust'],'adj_num': adj_num}# 使用 loc 或 append 方法将新行添加到新的 DataFrame 中new_df.loc[len(new_df)] = new_rowreturn new_df#Step2 清洗后的df到最终结果输出活动名、起始日期、天数(剥离工作日)
def df2res(df,text_input):# demo.launch()# 使用pandas读取上传的Excel文件#df = pd.read_excel(r'D:\temp222\数据格式.xlsx', sheet_name="Sheet1")# 将DataFrame转换为列表##df.columns = ["seq", "active", "days", "adjust"]df['cumulative'] = df['adj_num'].cumsum()# print(update_value('2025-04-03',88))##df['newday']=df.apply(update_value('2025-04-03', df['cumulative']), axis=1)df['prev_col1'] = df['cumulative'].shift(1).fillna(0).astype(int)fina_df = pd.DataFrame(columns=['active', 'start', 'end', 'diff'])for index, row in df.iterrows():if pd.notna(row['prev_col1']):# print(f"active: {row['active']}, cumulative: {row['cumulative']},prev_col1: {update_value('2025-04-03',row['prev_col1'])}, newday: {update_value('2025-04-03',row['cumulative'])}, ")pre = calculate_end_date(text_input, row['prev_col1'])curr = calculate_end_date(text_input, row['cumulative'])diff = business_days_diff(pre, curr)new_row = {'active': row['active'],'start': pre,'end': curr,'diff': diff}fina_df.loc[len(fina_df)] = new_row#print(f"{row['active']}\t{pre}\t{curr}\t {diff}")else:print(f"Current value: {row['cumulative']} (no previous value)")return fina_df##data_list = df.values.tolist()##print(data_list)# 创建Gradio界面
with gr.Blocks() as demo:file_input = gr.File(label="上传Excel文件")text_input = gr.Textbox(placeholder="YYYY-MM-DD",label="输入起始日期,默认是当前日期")###sheet_dropdown = gr.Dropdown(choices=[], label="Select Sheet", interactive=True)def update_sheets(file):if file is None:return gr.Dropdown.update(choices=[])else:sheets = get_sheet_names(file)return gr.Dropdown(choices=sheets, value=sheets[0] if sheets else None)###file_input.change(fn=update_sheets, inputs=[file_input], outputs=[sheet_dropdown])#output = gr.JSON()output_file = gr.File(label="生成的表格")btn = gr.Button("提交")##btn.click(fn=load_excel, inputs=[file_input], outputs=output)#btn.click(fn=load_excel, inputs=[file_input,text_input], outputs=output)btn.click(fn=load_excel, inputs=[file_input, text_input], outputs=output_file)###btn.click(fn=load_excel, inputs=[file_input, sheet_dropdown], outputs=output)
#
# #demo.launch()if __name__ == '__main__':demo.launch(server_name='0.0.0.0',server_port=8998)##df = pd.read_excel(r'D:\temp222\数据格式.xlsx', sheet_name="Sheet1")##print(excelclean(df))##print(df2res(excelclean(df),'2025-06-03'))
##----

相关文章:

  • idea 启动jar程序并调试
  • HTML前端开发:JavaScript 常用事件详解
  • JS的传统写法 vs 简写形式
  • Spring框架的设计模式
  • 打卡第39天:Dataset 和 Dataloader类
  • Spring Cloud 微服务架构实战指南 -- SpringCLoud概述
  • BeckHoff_FB --> SET_SNB 功能块
  • Halcon案例(三):C#联合Halcon识别排线
  • 【RAG召回】bge实现向量相似度索引
  • 四.抽象工厂模式
  • 如何在Spring Boot中使用注解动态切换实现
  • Kubernetes 节点资源驱逐策略详解:evictionHard 与 evictionSoft
  • remote display server is not supported (e.g. Wayland)
  • Vue中虚拟DOM的原理与作用
  • 【RTP】Intra-Refresh模式下的 H.264 输出,RTP打包的方式和普通 H.264 流并没有本质区别
  • python批量将文件夹下的excel转word文件
  • 海思Hi3798MV310_V39_HMS DDR3_安卓9.0_外贸盒update升级包
  • 深入了解NIO的优化实现原理
  • Linux运维新人自用笔记(乌班图apt命令和dpkg命令、两系统指令区别,rpm解决路径依赖、免安装配置java环境)
  • 发送文件脚本源码版本
  • 在演示文稿上网站怎么做/怎么创建一个属于自己的网站
  • 什么是网络营销?有何特点/百度seo优化公司
  • 开宾馆做独家网站好吗/推广渠道平台
  • 上海公司牌照价格最新价格/seo博客教程
  • dreamweaver的优点/抖音seo排名软件哪个好
  • 大连网站建设在线/百度开店怎么收费