|
|
@@ -26,50 +26,238 @@ import os
|
|
|
import psutil
|
|
|
import time
|
|
|
import tkinter as tk
|
|
|
-from tkinter import scrolledtext
|
|
|
+from tkinter import scrolledtext, messagebox, ttk
|
|
|
from playwright.async_api import async_playwright
|
|
|
+import requests
|
|
|
+import logging
|
|
|
+import threading
|
|
|
+import re
|
|
|
+
|
|
|
+# 配置日志
|
|
|
+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
+
|
|
|
+# 全局变量,用于控制生成状态
|
|
|
+is_generating = False
|
|
|
+
|
|
|
+def count_chinese_chars(text):
|
|
|
+ """统计中文字符数量"""
|
|
|
+ return len(re.findall(r'[\u4e00-\u9fff]', text))
|
|
|
+
|
|
|
+def count_words(text):
|
|
|
+ """统计总字数(中文字符+其他字符)"""
|
|
|
+ return count_chinese_chars(text) + len(re.sub(r'[\u4e00-\u9fff]', '', text))
|
|
|
+
|
|
|
+def generate_daily_report(prompt, ollama_url, model_name):
|
|
|
+ """使用Ollama生成日报内容"""
|
|
|
+ try:
|
|
|
+ logging.info(f"开始生成日报,使用模型: {model_name}")
|
|
|
+ # 生成今日工作总结
|
|
|
+ summary_response = requests.post(
|
|
|
+ f"{ollama_url}/api/generate",
|
|
|
+ json={
|
|
|
+ "model": model_name,
|
|
|
+ "prompt": f"你是一个专业的日报生成助手,请根据以下工作内容生成专业的今日工作总结:{prompt}",
|
|
|
+ "stream": False
|
|
|
+ }
|
|
|
+ )
|
|
|
+ logging.info(f"今日工作总结生成状态码: {summary_response.status_code}")
|
|
|
+
|
|
|
+ # 生成明日工作计划
|
|
|
+ plan_response = requests.post(
|
|
|
+ f"{ollama_url}/api/generate",
|
|
|
+ json={
|
|
|
+ "model": model_name,
|
|
|
+ "prompt": f"你是一个专业的日报生成助手,请根据以下工作内容生成合理的明日工作计划:{prompt}",
|
|
|
+ "stream": False
|
|
|
+ }
|
|
|
+ )
|
|
|
+ logging.info(f"明日工作计划生成状态码: {plan_response.status_code}")
|
|
|
+
|
|
|
+ if summary_response.status_code == 200 and plan_response.status_code == 200:
|
|
|
+ return summary_response.json()["response"], plan_response.json()["response"]
|
|
|
+ else:
|
|
|
+ error_msg = f"API调用失败: 总结状态码={summary_response.status_code}, 计划状态码={plan_response.status_code}"
|
|
|
+ if summary_response.status_code != 200:
|
|
|
+ error_msg += f"\n总结生成失败原因: {summary_response.text}"
|
|
|
+ if plan_response.status_code != 200:
|
|
|
+ error_msg += f"\n计划生成失败原因: {plan_response.text}"
|
|
|
+ logging.error(error_msg)
|
|
|
+ return None, error_msg
|
|
|
+ except Exception as e:
|
|
|
+ error_msg = f"生成日报时发生错误: {str(e)}"
|
|
|
+ logging.error(error_msg)
|
|
|
+ return None, error_msg
|
|
|
|
|
|
def get_user_input():
|
|
|
"""通过GUI窗口获取用户输入的日报内容"""
|
|
|
root = tk.Tk()
|
|
|
root.title("日报填写助手")
|
|
|
- root.geometry("900x700")
|
|
|
+ root.geometry("1000x800") # 设置更大的初始窗口大小
|
|
|
|
|
|
# 设置窗口样式
|
|
|
root.configure(bg='#f0f0f0')
|
|
|
- root.option_add('*Font', '微软雅黑 10')
|
|
|
+ # 修改字体设置方式
|
|
|
+ default_font = ('微软雅黑', 10)
|
|
|
+ root.option_add('*Font', default_font)
|
|
|
|
|
|
# 设置窗口图标(如果有的话)
|
|
|
try:
|
|
|
- root.iconbitmap('icon.ico') # 如果有图标文件的话
|
|
|
+ root.iconbitmap('icon.ico')
|
|
|
except:
|
|
|
pass
|
|
|
|
|
|
# 创建主框架
|
|
|
- main_frame = tk.Frame(root, bg='#f0f0f0', padx=20, pady=20)
|
|
|
- main_frame.pack(fill=tk.BOTH, expand=True)
|
|
|
+ main_frame = tk.Frame(root, bg='#f0f0f0')
|
|
|
+ main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
|
|
|
|
# 创建标题
|
|
|
title_label = tk.Label(main_frame,
|
|
|
text="日报填写",
|
|
|
- font=("微软雅黑", 16, "bold"),
|
|
|
+ font=('微软雅黑', 16, 'bold'),
|
|
|
bg='#f0f0f0',
|
|
|
- pady=10)
|
|
|
+ pady=5)
|
|
|
title_label.pack()
|
|
|
|
|
|
+ # 创建配置区域框架
|
|
|
+ config_frame = tk.Frame(main_frame, bg='#f0f0f0')
|
|
|
+ config_frame.pack(fill=tk.X, pady=5)
|
|
|
+
|
|
|
+ # 创建Ollama配置区域
|
|
|
+ ollama_frame = tk.LabelFrame(config_frame,
|
|
|
+ text="Ollama配置",
|
|
|
+ font=('微软雅黑', 12),
|
|
|
+ bg='#f0f0f0',
|
|
|
+ padx=10,
|
|
|
+ pady=5)
|
|
|
+ ollama_frame.pack(fill=tk.X, pady=2)
|
|
|
+
|
|
|
+ # Ollama地址输入
|
|
|
+ url_frame = tk.Frame(ollama_frame, bg='#f0f0f0')
|
|
|
+ url_frame.pack(fill=tk.X, pady=2)
|
|
|
+ tk.Label(url_frame,
|
|
|
+ text="Ollama地址:",
|
|
|
+ font=('微软雅黑', 11),
|
|
|
+ bg='#f0f0f0').pack(side=tk.LEFT)
|
|
|
+ ollama_url = tk.Entry(url_frame,
|
|
|
+ font=('微软雅黑', 11))
|
|
|
+ ollama_url.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
|
|
+ ollama_url.insert(0, "http://localhost:11434")
|
|
|
+
|
|
|
+ # 模型名称输入
|
|
|
+ model_frame = tk.Frame(ollama_frame, bg='#f0f0f0')
|
|
|
+ model_frame.pack(fill=tk.X, pady=2)
|
|
|
+ tk.Label(model_frame,
|
|
|
+ text="模型名称:",
|
|
|
+ font=('微软雅黑', 11),
|
|
|
+ bg='#f0f0f0').pack(side=tk.LEFT)
|
|
|
+ model_name = tk.Entry(model_frame,
|
|
|
+ font=('微软雅黑', 11))
|
|
|
+ model_name.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
|
|
+ model_name.insert(0, "llama2")
|
|
|
+
|
|
|
+ # 创建提示词输入区域
|
|
|
+ prompt_frame = tk.LabelFrame(config_frame,
|
|
|
+ text="工作内容提示词",
|
|
|
+ font=('微软雅黑', 12),
|
|
|
+ bg='#f0f0f0',
|
|
|
+ padx=10,
|
|
|
+ pady=5)
|
|
|
+ prompt_frame.pack(fill=tk.X, pady=2)
|
|
|
+
|
|
|
+ prompt_text = scrolledtext.ScrolledText(prompt_frame,
|
|
|
+ height=3,
|
|
|
+ font=('微软雅黑', 11),
|
|
|
+ wrap=tk.WORD,
|
|
|
+ padx=10,
|
|
|
+ pady=10)
|
|
|
+ prompt_text.pack(fill=tk.X, expand=True)
|
|
|
+
|
|
|
+ # 创建进度条
|
|
|
+ progress_var = tk.DoubleVar()
|
|
|
+ progress_bar = ttk.Progressbar(prompt_frame,
|
|
|
+ variable=progress_var,
|
|
|
+ maximum=100,
|
|
|
+ mode='indeterminate')
|
|
|
+
|
|
|
+ # 创建自动生成按钮
|
|
|
+ def auto_generate():
|
|
|
+ global is_generating
|
|
|
+ if is_generating:
|
|
|
+ return
|
|
|
+
|
|
|
+ prompt = prompt_text.get("1.0", tk.END).strip()
|
|
|
+ if not prompt:
|
|
|
+ messagebox.showwarning("提示", "请输入工作内容提示词")
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ is_generating = True
|
|
|
+ auto_generate_button.config(state=tk.DISABLED)
|
|
|
+ progress_bar.pack(fill=tk.X, pady=5)
|
|
|
+ progress_bar.start()
|
|
|
+
|
|
|
+ def generate_thread():
|
|
|
+ try:
|
|
|
+ summary, plan = generate_daily_report(
|
|
|
+ prompt,
|
|
|
+ ollama_url.get().strip(),
|
|
|
+ model_name.get().strip()
|
|
|
+ )
|
|
|
+ if summary and plan:
|
|
|
+ root.after(0, lambda: update_texts(summary, plan))
|
|
|
+ else:
|
|
|
+ root.after(0, lambda: messagebox.showerror("错误", f"生成日报失败:{plan}"))
|
|
|
+ except Exception as e:
|
|
|
+ root.after(0, lambda: messagebox.showerror("错误", f"生成日报时发生错误:{str(e)}"))
|
|
|
+ finally:
|
|
|
+ root.after(0, finish_generation)
|
|
|
+
|
|
|
+ threading.Thread(target=generate_thread, daemon=True).start()
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ messagebox.showerror("错误", f"生成日报时发生错误:{str(e)}")
|
|
|
+ finish_generation()
|
|
|
+
|
|
|
+ def update_texts(summary, plan):
|
|
|
+ summary_text.delete("1.0", tk.END)
|
|
|
+ plan_text.delete("1.0", tk.END)
|
|
|
+ summary_text.insert("1.0", summary)
|
|
|
+ plan_text.insert("1.0", plan)
|
|
|
+ update_counts()
|
|
|
+
|
|
|
+ def finish_generation():
|
|
|
+ global is_generating
|
|
|
+ is_generating = False
|
|
|
+ auto_generate_button.config(state=tk.NORMAL)
|
|
|
+ progress_bar.stop()
|
|
|
+ progress_bar.pack_forget()
|
|
|
+
|
|
|
+ auto_generate_button = tk.Button(prompt_frame,
|
|
|
+ text="自动生成日报",
|
|
|
+ command=auto_generate,
|
|
|
+ font=('微软雅黑', 11),
|
|
|
+ bg='#2196F3',
|
|
|
+ fg='white',
|
|
|
+ relief=tk.FLAT,
|
|
|
+ cursor='hand2')
|
|
|
+ auto_generate_button.pack(pady=5)
|
|
|
+
|
|
|
+ # 创建内容区域框架
|
|
|
+ content_frame = tk.Frame(main_frame, bg='#f0f0f0')
|
|
|
+ content_frame.pack(fill=tk.BOTH, expand=True, pady=5)
|
|
|
+
|
|
|
# 创建今日工作总结输入区域
|
|
|
- summary_frame = tk.LabelFrame(main_frame,
|
|
|
+ summary_frame = tk.LabelFrame(content_frame,
|
|
|
text="今日工作总结",
|
|
|
- font=("微软雅黑", 12),
|
|
|
+ font=('微软雅黑', 12),
|
|
|
bg='#f0f0f0',
|
|
|
padx=10,
|
|
|
pady=5)
|
|
|
- summary_frame.pack(fill=tk.BOTH, expand=True, pady=10)
|
|
|
+ summary_frame.pack(fill=tk.BOTH, expand=True, pady=2)
|
|
|
|
|
|
summary_text = scrolledtext.ScrolledText(summary_frame,
|
|
|
- width=80,
|
|
|
- height=8,
|
|
|
- font=("微软雅黑", 11),
|
|
|
+ height=8,
|
|
|
+ font=('微软雅黑', 11),
|
|
|
wrap=tk.WORD,
|
|
|
padx=10,
|
|
|
pady=10)
|
|
|
@@ -78,24 +266,23 @@ def get_user_input():
|
|
|
# 添加字数统计
|
|
|
summary_count = tk.Label(summary_frame,
|
|
|
text="字数:0",
|
|
|
- font=("微软雅黑", 10),
|
|
|
+ font=('微软雅黑', 10),
|
|
|
bg='#f0f0f0',
|
|
|
anchor='e')
|
|
|
summary_count.pack(fill=tk.X, padx=10)
|
|
|
|
|
|
# 创建明日工作计划输入区域
|
|
|
- plan_frame = tk.LabelFrame(main_frame,
|
|
|
+ plan_frame = tk.LabelFrame(content_frame,
|
|
|
text="明日工作计划",
|
|
|
- font=("微软雅黑", 12),
|
|
|
+ font=('微软雅黑', 12),
|
|
|
bg='#f0f0f0',
|
|
|
padx=10,
|
|
|
pady=5)
|
|
|
- plan_frame.pack(fill=tk.BOTH, expand=True, pady=10)
|
|
|
+ plan_frame.pack(fill=tk.BOTH, expand=True, pady=2)
|
|
|
|
|
|
plan_text = scrolledtext.ScrolledText(plan_frame,
|
|
|
- width=80,
|
|
|
- height=8,
|
|
|
- font=("微软雅黑", 11),
|
|
|
+ height=8,
|
|
|
+ font=('微软雅黑', 11),
|
|
|
wrap=tk.WORD,
|
|
|
padx=10,
|
|
|
pady=10)
|
|
|
@@ -104,27 +291,31 @@ def get_user_input():
|
|
|
# 添加字数统计
|
|
|
plan_count = tk.Label(plan_frame,
|
|
|
text="字数:0",
|
|
|
- font=("微软雅黑", 10),
|
|
|
+ font=('微软雅黑', 10),
|
|
|
bg='#f0f0f0',
|
|
|
anchor='e')
|
|
|
plan_count.pack(fill=tk.X, padx=10)
|
|
|
|
|
|
# 创建底部控制区域
|
|
|
- bottom_frame = tk.Frame(main_frame, bg='#f0f0f0', pady=10)
|
|
|
- bottom_frame.pack(fill=tk.X)
|
|
|
+ bottom_frame = tk.Frame(main_frame, bg='#f0f0f0')
|
|
|
+ bottom_frame.pack(fill=tk.X, pady=5)
|
|
|
|
|
|
# 创建复选框
|
|
|
keep_original = tk.BooleanVar(value=False)
|
|
|
check_button = tk.Checkbutton(bottom_frame,
|
|
|
text="保留原有内容",
|
|
|
variable=keep_original,
|
|
|
- font=("微软雅黑", 11),
|
|
|
+ font=('微软雅黑', 11),
|
|
|
bg='#f0f0f0')
|
|
|
check_button.pack(side=tk.LEFT, padx=10)
|
|
|
|
|
|
# 创建提交按钮
|
|
|
result = {"summary": "", "plan": "", "is_submitted": False, "keep_original": False}
|
|
|
def submit():
|
|
|
+ if not summary_text.get("1.0", tk.END).strip() or not plan_text.get("1.0", tk.END).strip():
|
|
|
+ messagebox.showwarning("提示", "请填写完整的工作总结和计划")
|
|
|
+ return
|
|
|
+
|
|
|
result["summary"] = summary_text.get("1.0", tk.END).strip()
|
|
|
result["plan"] = plan_text.get("1.0", tk.END).strip()
|
|
|
result["keep_original"] = keep_original.get()
|
|
|
@@ -135,18 +326,21 @@ def get_user_input():
|
|
|
submit_button = tk.Button(bottom_frame,
|
|
|
text="提交",
|
|
|
command=submit,
|
|
|
- font=("微软雅黑", 12, "bold"),
|
|
|
+ font=('微软雅黑', 12, 'bold'),
|
|
|
bg='#4CAF50',
|
|
|
fg='white',
|
|
|
width=15,
|
|
|
height=2,
|
|
|
- relief=tk.FLAT)
|
|
|
+ relief=tk.FLAT,
|
|
|
+ cursor='hand2')
|
|
|
submit_button.pack(side=tk.RIGHT, padx=10)
|
|
|
|
|
|
# 添加字数统计更新函数
|
|
|
def update_counts(event=None):
|
|
|
- summary_count.config(text=f"字数:{len(summary_text.get('1.0', tk.END).strip())}")
|
|
|
- plan_count.config(text=f"字数:{len(plan_text.get('1.0', tk.END).strip())}")
|
|
|
+ summary_text_content = summary_text.get("1.0", tk.END).strip()
|
|
|
+ plan_text_content = plan_text.get("1.0", tk.END).strip()
|
|
|
+ summary_count.config(text=f"字数:{count_words(summary_text_content)}(中文字符:{count_chinese_chars(summary_text_content)})")
|
|
|
+ plan_count.config(text=f"字数:{count_words(plan_text_content)}(中文字符:{count_chinese_chars(plan_text_content)})")
|
|
|
|
|
|
# 绑定字数统计更新
|
|
|
summary_text.bind('<KeyRelease>', update_counts)
|
|
|
@@ -163,6 +357,19 @@ def get_user_input():
|
|
|
y = (root.winfo_screenheight() // 2) - (height // 2)
|
|
|
root.geometry(f'{width}x{height}+{x}+{y}')
|
|
|
|
|
|
+ # 设置窗口最小尺寸
|
|
|
+ root.minsize(900, 700)
|
|
|
+
|
|
|
+ # 设置窗口关闭确认
|
|
|
+ def on_closing():
|
|
|
+ if messagebox.askokcancel("确认", "确定要关闭窗口吗?"):
|
|
|
+ root.destroy()
|
|
|
+
|
|
|
+ root.protocol("WM_DELETE_WINDOW", on_closing)
|
|
|
+
|
|
|
+ # 设置默认焦点
|
|
|
+ prompt_text.focus_set()
|
|
|
+
|
|
|
root.mainloop()
|
|
|
return result["summary"], result["plan"], result["is_submitted"], result["keep_original"]
|
|
|
|