|
|
@ -1,5 +1,6 @@ |
|
|
import os |
|
|
import os |
|
|
import json |
|
|
import json |
|
|
|
|
|
import math |
|
|
import sqlite3 |
|
|
import sqlite3 |
|
|
import webbrowser |
|
|
import webbrowser |
|
|
from datetime import datetime, date, timezone |
|
|
from datetime import datetime, date, timezone |
|
|
@ -12,7 +13,7 @@ from tkcalendar import Calendar |
|
|
|
|
|
|
|
|
# ----------------------- CONFIG ----------------------- |
|
|
# ----------------------- CONFIG ----------------------- |
|
|
# region 配置 |
|
|
# region 配置 |
|
|
VERSION = "v1.0.0" |
|
|
VERSION = "v1.0.1" |
|
|
|
|
|
|
|
|
DB_PATH = "tasks.db" # 数据库文件路径 |
|
|
DB_PATH = "tasks.db" # 数据库文件路径 |
|
|
TEMPLATES_PATH = "templates.json" # 检索模板文件路径 |
|
|
TEMPLATES_PATH = "templates.json" # 检索模板文件路径 |
|
|
@ -91,6 +92,7 @@ COLUMNS = [ |
|
|
("type", "类型"), |
|
|
("type", "类型"), |
|
|
("status", "状态"), |
|
|
("status", "状态"), |
|
|
("priority", "优先级"), |
|
|
("priority", "优先级"), |
|
|
|
|
|
("deadline", "截止日期"), |
|
|
("composite", "综合优先级"), |
|
|
("composite", "综合优先级"), |
|
|
("title", "标题"), |
|
|
("title", "标题"), |
|
|
("brief", "简介"), |
|
|
("brief", "简介"), |
|
|
@ -115,6 +117,19 @@ COL_START_DATE = COL_INDEX.get("start_date") |
|
|
COL_LINKS_COUNT = COL_INDEX.get("links_count") |
|
|
COL_LINKS_COUNT = COL_INDEX.get("links_count") |
|
|
COL_NOTES = COL_INDEX.get("notes") |
|
|
COL_NOTES = COL_INDEX.get("notes") |
|
|
COL_COMPOSITE = COL_INDEX.get("composite") |
|
|
COL_COMPOSITE = COL_INDEX.get("composite") |
|
|
|
|
|
COL_DEADLINE = COL_INDEX.get("deadline") |
|
|
|
|
|
|
|
|
|
|
|
# 截止日期颜色,距离截止日期的时间小于哪个值就是哪个颜色 |
|
|
|
|
|
DEADLINE_COLOR = [ |
|
|
|
|
|
(0, "#fb0000"), |
|
|
|
|
|
(1, "#ff6f22"), |
|
|
|
|
|
(2, "#ff904b"), |
|
|
|
|
|
(3, "#b2ff77"), |
|
|
|
|
|
(4, "#73bc75"), |
|
|
|
|
|
(10, "#83d6ff"), |
|
|
|
|
|
(15, "#bdd8e9"), |
|
|
|
|
|
(30, "#ffffff"), |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
# endregion |
|
|
# endregion |
|
|
|
|
|
|
|
|
@ -141,6 +156,7 @@ class TaskDB: |
|
|
type TEXT, |
|
|
type TEXT, |
|
|
status TEXT, |
|
|
status TEXT, |
|
|
start_date TEXT, |
|
|
start_date TEXT, |
|
|
|
|
|
deadline TEXT, |
|
|
links TEXT, |
|
|
links TEXT, |
|
|
notes TEXT, |
|
|
notes TEXT, |
|
|
updated_at TEXT |
|
|
updated_at TEXT |
|
|
@ -178,6 +194,8 @@ class TaskDB: |
|
|
adds.append(("status", "TEXT")) |
|
|
adds.append(("status", "TEXT")) |
|
|
if "start_date" not in cols: |
|
|
if "start_date" not in cols: |
|
|
adds.append(("start_date", "TEXT")) |
|
|
adds.append(("start_date", "TEXT")) |
|
|
|
|
|
if "deadline" not in cols: |
|
|
|
|
|
adds.append(("deadline", "TEXT")) |
|
|
if "links" not in cols: |
|
|
if "links" not in cols: |
|
|
adds.append(("links", "TEXT")) |
|
|
adds.append(("links", "TEXT")) |
|
|
if "notes" not in cols: |
|
|
if "notes" not in cols: |
|
|
@ -224,8 +242,8 @@ class TaskDB: |
|
|
now = datetime.now(timezone.utc).isoformat() |
|
|
now = datetime.now(timezone.utc).isoformat() |
|
|
cur = self.conn.cursor() |
|
|
cur = self.conn.cursor() |
|
|
cur.execute(""" |
|
|
cur.execute(""" |
|
|
INSERT INTO tasks (title, brief, description, priority, type, status, start_date, links, notes, updated_at) |
|
|
INSERT INTO tasks (title, brief, description, priority, type, status, start_date, deadline, links, notes, updated_at) |
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |
|
|
""", ( |
|
|
""", ( |
|
|
task.get("title", ""), |
|
|
task.get("title", ""), |
|
|
task.get("brief", ""), |
|
|
task.get("brief", ""), |
|
|
@ -234,6 +252,7 @@ class TaskDB: |
|
|
task.get("type"), |
|
|
task.get("type"), |
|
|
task.get("status"), |
|
|
task.get("status"), |
|
|
task.get("start_date"), |
|
|
task.get("start_date"), |
|
|
|
|
|
task.get("deadline"), |
|
|
json.dumps(task.get("links", []), ensure_ascii=False), |
|
|
json.dumps(task.get("links", []), ensure_ascii=False), |
|
|
task.get("notes", ""), |
|
|
task.get("notes", ""), |
|
|
now |
|
|
now |
|
|
@ -244,7 +263,7 @@ class TaskDB: |
|
|
def update_task(self, tid: int, task: dict): |
|
|
def update_task(self, tid: int, task: dict): |
|
|
now = datetime.now(timezone.utc).isoformat() |
|
|
now = datetime.now(timezone.utc).isoformat() |
|
|
self.conn.execute(""" |
|
|
self.conn.execute(""" |
|
|
UPDATE tasks SET title=?, brief=?, description=?, priority=?, type=?, status=?, start_date=?, links=?, notes=?, updated_at=? |
|
|
UPDATE tasks SET title=?, brief=?, description=?, priority=?, type=?, status=?, start_date=?, deadline=?, links=?, notes=?, updated_at=? |
|
|
WHERE sid=? |
|
|
WHERE sid=? |
|
|
""", ( |
|
|
""", ( |
|
|
task.get("title", ""), |
|
|
task.get("title", ""), |
|
|
@ -254,6 +273,7 @@ class TaskDB: |
|
|
task.get("type"), |
|
|
task.get("type"), |
|
|
task.get("status"), |
|
|
task.get("status"), |
|
|
task.get("start_date"), |
|
|
task.get("start_date"), |
|
|
|
|
|
task.get("deadline"), |
|
|
json.dumps(task.get("links", []), ensure_ascii=False), |
|
|
json.dumps(task.get("links", []), ensure_ascii=False), |
|
|
task.get("notes", ""), |
|
|
task.get("notes", ""), |
|
|
now, |
|
|
now, |
|
|
@ -363,24 +383,37 @@ def compute_composite_score(task_row: dict) -> float: |
|
|
Compute composite priority score using COMPOSITE_WEIGHTS. |
|
|
Compute composite priority score using COMPOSITE_WEIGHTS. |
|
|
Higher number => more urgent. |
|
|
Higher number => more urgent. |
|
|
""" |
|
|
""" |
|
|
# 读取数据 |
|
|
# 任务开始以来的天数 |
|
|
start_date = task_row.get("start_date") |
|
|
start_date = task_row.get("start_date") |
|
|
days = 0 |
|
|
days_from_start = 0 |
|
|
try: |
|
|
try: |
|
|
if start_date: |
|
|
if start_date: |
|
|
dt = datetime.fromisoformat(start_date) |
|
|
dt = datetime.fromisoformat(start_date) |
|
|
# convert to local naive days |
|
|
# convert to local naive days |
|
|
days = (datetime.now(dt.tzinfo or timezone.utc).date() - dt.date()).days |
|
|
days_from_start = (datetime.now(dt.tzinfo or timezone.utc).date() - dt.date()).days |
|
|
if days < 0: |
|
|
if days_from_start < 0: |
|
|
days = 0 |
|
|
days_from_start = 0 |
|
|
|
|
|
except Exception: |
|
|
|
|
|
days_from_start = 0 |
|
|
|
|
|
# 距离截止日期的时间 |
|
|
|
|
|
deadline = task_row.get("deadline") |
|
|
|
|
|
have_deadline = False |
|
|
|
|
|
days_to_deadline = 0 |
|
|
|
|
|
try: |
|
|
|
|
|
if deadline: |
|
|
|
|
|
dt_deadline = datetime.fromisoformat(deadline) |
|
|
|
|
|
days_to_deadline = (dt_deadline.date() - datetime.now(dt_deadline.tzinfo or timezone.utc).date()).days |
|
|
|
|
|
have_deadline = True |
|
|
except Exception: |
|
|
except Exception: |
|
|
days = 0 |
|
|
have_deadline = False |
|
|
d_score = COMPOSITE_WEIGHTS.get("age_factor", 0.1) * days + 1 |
|
|
# 各种因素分数 |
|
|
|
|
|
ddl_score = 1 + math.exp(-(0.3 + days_to_deadline / 3.0)) if have_deadline else 0.8 |
|
|
|
|
|
d_score = COMPOSITE_WEIGHTS.get("age_factor", 0.1) * days_from_start + 1 |
|
|
p_score = COMPOSITE_WEIGHTS["priority"].get(task_row.get("priority"), 1.0) |
|
|
p_score = COMPOSITE_WEIGHTS["priority"].get(task_row.get("priority"), 1.0) |
|
|
t_score = COMPOSITE_WEIGHTS["type"].get(task_row.get("type"), 1.0) |
|
|
t_score = COMPOSITE_WEIGHTS["type"].get(task_row.get("type"), 1.0) |
|
|
s_score = COMPOSITE_WEIGHTS["status"].get(task_row.get("status"), 1.0) |
|
|
s_score = COMPOSITE_WEIGHTS["status"].get(task_row.get("status"), 1.0) |
|
|
# 计算综合优先级分数 |
|
|
# 计算综合优先级分数 |
|
|
score = p_score * t_score * s_score * d_score |
|
|
score = p_score * t_score * s_score * d_score * ddl_score |
|
|
return round(float(score), 6) |
|
|
return round(float(score), 6) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -401,6 +434,7 @@ class TaskEditor(ctk.CTkToplevel): |
|
|
self.title_var = ctk.StringVar() |
|
|
self.title_var = ctk.StringVar() |
|
|
self.brief_var = ctk.StringVar() |
|
|
self.brief_var = ctk.StringVar() |
|
|
self.start_var = ctk.StringVar(value=date.today().isoformat()) |
|
|
self.start_var = ctk.StringVar(value=date.today().isoformat()) |
|
|
|
|
|
self.deadline_var = ctk.StringVar(value="") |
|
|
self.priority_var = ctk.StringVar() |
|
|
self.priority_var = ctk.StringVar() |
|
|
self.type_var = ctk.StringVar() |
|
|
self.type_var = ctk.StringVar() |
|
|
self.status_var = ctk.StringVar() |
|
|
self.status_var = ctk.StringVar() |
|
|
@ -446,33 +480,37 @@ class TaskEditor(ctk.CTkToplevel): |
|
|
ctk.CTkLabel(frm, text="Start Date (YYYY-MM-DD)").grid(row=8, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
ctk.CTkLabel(frm, text="Start Date (YYYY-MM-DD)").grid(row=8, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
ctk.CTkEntry(frm, textvariable=self.start_var).grid(row=9, column=0, columnspan=3, sticky="ew", padx=pad) |
|
|
ctk.CTkEntry(frm, textvariable=self.start_var).grid(row=9, column=0, columnspan=3, sticky="ew", padx=pad) |
|
|
|
|
|
|
|
|
|
|
|
# deadline |
|
|
|
|
|
ctk.CTkLabel(frm, text="Deadline (YYYY-MM-DD)").grid(row=10, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
|
|
|
ctk.CTkEntry(frm, textvariable=self.deadline_var).grid(row=11, column=0, columnspan=3, sticky="ew", padx=pad) |
|
|
|
|
|
|
|
|
# links |
|
|
# links |
|
|
ctk.CTkLabel(frm, text="Links (one per line)").grid(row=10, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
ctk.CTkLabel(frm, text="Links (one per line)").grid(row=12, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
self.links_text = ctk.CTkTextbox(frm, height=80) |
|
|
self.links_text = ctk.CTkTextbox(frm, height=80) |
|
|
self.links_text.grid(row=11, column=0, columnspan=3, sticky="nsew", padx=pad) |
|
|
self.links_text.grid(row=13, column=0, columnspan=3, sticky="nsew", padx=pad) |
|
|
|
|
|
|
|
|
# logs (processed dates) |
|
|
# logs (processed dates) |
|
|
ctk.CTkLabel(frm, text="Processed Dates (handling records)").grid(row=12, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
ctk.CTkLabel(frm, text="Processed Dates (handling records)").grid(row=14, column=0, columnspan=3, sticky="w", padx=pad, pady=(10,0)) |
|
|
logs_frame = ctk.CTkFrame(frm) |
|
|
logs_frame = ctk.CTkFrame(frm) |
|
|
logs_frame.grid(row=13, column=0, columnspan=3, sticky="ew", padx=pad, pady=(4,0)) |
|
|
logs_frame.grid(row=15, column=0, columnspan=3, sticky="ew", padx=pad, pady=(4,0)) |
|
|
ctk.CTkButton(logs_frame, text="Mark Today", width=120, command=self._mark_today).pack(side="left", padx=6, pady=6) |
|
|
ctk.CTkButton(logs_frame, text="Mark Today", width=120, command=self._mark_today).pack(side="left", padx=6, pady=6) |
|
|
ctk.CTkButton(logs_frame, text="Add Date...", width=120, command=self._add_date_dialog).pack(side="left", padx=6, pady=6) |
|
|
ctk.CTkButton(logs_frame, text="Add Date...", width=120, command=self._add_date_dialog).pack(side="left", padx=6, pady=6) |
|
|
ctk.CTkButton(logs_frame, text="Remove Selected", fg_color="#cc4444", hover_color="#aa3333", command=self._remove_selected_date).pack(side="right", padx=6, pady=6) |
|
|
ctk.CTkButton(logs_frame, text="Remove Selected", fg_color="#cc4444", hover_color="#aa3333", command=self._remove_selected_date).pack(side="right", padx=6, pady=6) |
|
|
|
|
|
|
|
|
# list widget for logs (tk Listbox embedded) |
|
|
# list widget for logs (tk Listbox embedded) |
|
|
self.logs_listbox = tk.Listbox(frm, height=5) |
|
|
self.logs_listbox = tk.Listbox(frm, height=5) |
|
|
self.logs_listbox.grid(row=14, column=0, columnspan=3, sticky="nsew", padx=pad, pady=(4,0)) |
|
|
self.logs_listbox.grid(row=16, column=0, columnspan=3, sticky="nsew", padx=pad, pady=(4,0)) |
|
|
|
|
|
|
|
|
# buttons |
|
|
# buttons |
|
|
btn_row = ctk.CTkFrame(frm) |
|
|
btn_row = ctk.CTkFrame(frm) |
|
|
btn_row.grid(row=15, column=0, columnspan=3, pady=(12,6)) |
|
|
btn_row.grid(row=17, column=0, columnspan=3, pady=(12,6)) |
|
|
ctk.CTkButton(btn_row, text="Save", command=self._save).pack(side="left", padx=8) |
|
|
ctk.CTkButton(btn_row, text="Save", command=self._save).pack(side="left", padx=8) |
|
|
ctk.CTkButton(btn_row, text="Cancel", fg_color="#888", hover_color="#666", command=self.destroy).pack(side="left", padx=8) |
|
|
ctk.CTkButton(btn_row, text="Cancel", fg_color="#888", hover_color="#666", command=self.destroy).pack(side="left", padx=8) |
|
|
|
|
|
|
|
|
# grid weights |
|
|
# grid weights |
|
|
frm.grid_rowconfigure(5, weight=1) |
|
|
frm.grid_rowconfigure(5, weight=1) |
|
|
frm.grid_rowconfigure(11, weight=0) |
|
|
frm.grid_rowconfigure(13, weight=0) |
|
|
frm.grid_rowconfigure(14, weight=0) |
|
|
frm.grid_rowconfigure(16, weight=0) |
|
|
frm.grid_columnconfigure(0, weight=1) |
|
|
frm.grid_columnconfigure(0, weight=1) |
|
|
frm.grid_columnconfigure(1, weight=1) |
|
|
frm.grid_columnconfigure(1, weight=1) |
|
|
frm.grid_columnconfigure(2, weight=1) |
|
|
frm.grid_columnconfigure(2, weight=1) |
|
|
@ -489,6 +527,7 @@ class TaskEditor(ctk.CTkToplevel): |
|
|
self.type_cb.set(row.get("type") or "") |
|
|
self.type_cb.set(row.get("type") or "") |
|
|
self.status_cb.set(row.get("status") or "") |
|
|
self.status_cb.set(row.get("status") or "") |
|
|
self.start_var.set(row.get("start_date") or "") |
|
|
self.start_var.set(row.get("start_date") or "") |
|
|
|
|
|
self.deadline_var.set(row.get("deadline") or "") |
|
|
links = json.loads(row.get("links") or "[]") |
|
|
links = json.loads(row.get("links") or "[]") |
|
|
self.links_text.delete("1.0", "end"); self.links_text.insert("1.0", "\n".join(links)) |
|
|
self.links_text.delete("1.0", "end"); self.links_text.insert("1.0", "\n".join(links)) |
|
|
# logs |
|
|
# logs |
|
|
@ -556,6 +595,7 @@ class TaskEditor(ctk.CTkToplevel): |
|
|
"type": self.type_cb.get() or None, |
|
|
"type": self.type_cb.get() or None, |
|
|
"status": self.status_cb.get() or None, |
|
|
"status": self.status_cb.get() or None, |
|
|
"start_date": self.start_var.get().strip() or date.today().isoformat(), |
|
|
"start_date": self.start_var.get().strip() or date.today().isoformat(), |
|
|
|
|
|
"deadline": self.deadline_var.get().strip() or "", |
|
|
"links": links, |
|
|
"links": links, |
|
|
"processed_dates": getattr(self, "_logs", []) |
|
|
"processed_dates": getattr(self, "_logs", []) |
|
|
} |
|
|
} |
|
|
@ -575,6 +615,7 @@ class TaskEditor(ctk.CTkToplevel): |
|
|
"type": data["type"], |
|
|
"type": data["type"], |
|
|
"status": data["status"], |
|
|
"status": data["status"], |
|
|
"start_date": data["start_date"], |
|
|
"start_date": data["start_date"], |
|
|
|
|
|
"deadline": data["deadline"], |
|
|
"links": data["links"], |
|
|
"links": data["links"], |
|
|
"notes": data["notes"] |
|
|
"notes": data["notes"] |
|
|
}) |
|
|
}) |
|
|
@ -595,6 +636,7 @@ class TaskEditor(ctk.CTkToplevel): |
|
|
"type": data["type"], |
|
|
"type": data["type"], |
|
|
"status": data["status"], |
|
|
"status": data["status"], |
|
|
"start_date": data["start_date"], |
|
|
"start_date": data["start_date"], |
|
|
|
|
|
"deadline": data["deadline"], |
|
|
"links": data["links"], |
|
|
"links": data["links"], |
|
|
"notes": data["notes"], |
|
|
"notes": data["notes"], |
|
|
}) |
|
|
}) |
|
|
@ -725,7 +767,7 @@ class TaskManagerApp(ctk.CTk): |
|
|
# 指定列居中 |
|
|
# 指定列居中 |
|
|
align_center_cols = [] |
|
|
align_center_cols = [] |
|
|
for i, (key, _) in enumerate(COLUMNS): |
|
|
for i, (key, _) in enumerate(COLUMNS): |
|
|
if key in ('sid', 'priority', 'type', 'status', 'start_date', 'processed_today', 'last_processed', 'links_count', 'composite'): |
|
|
if key in ('sid', 'priority', 'type', 'status', 'start_date', 'processed_today', 'last_processed', 'links_count', 'composite', 'deadline'): |
|
|
align_center_cols.append(i) |
|
|
align_center_cols.append(i) |
|
|
self.sheet.align_columns(columns=align_center_cols, align="center") |
|
|
self.sheet.align_columns(columns=align_center_cols, align="center") |
|
|
|
|
|
|
|
|
@ -758,6 +800,20 @@ class TaskManagerApp(ctk.CTk): |
|
|
elif col == "processed_today": |
|
|
elif col == "processed_today": |
|
|
today_iso = date.today().isoformat() |
|
|
today_iso = date.today().isoformat() |
|
|
rows.sort(key=lambda r: (today_iso in self.db.get_logs_for_task(r["sid"])), reverse=not self.order_asc) |
|
|
rows.sort(key=lambda r: (today_iso in self.db.get_logs_for_task(r["sid"])), reverse=not self.order_asc) |
|
|
|
|
|
elif col == "deadline": |
|
|
|
|
|
# Treat empty/null deadlines as greater than any date so they appear last when sorting ascending |
|
|
|
|
|
def _deadline_key(r): |
|
|
|
|
|
d = r.get("deadline") |
|
|
|
|
|
if not d: |
|
|
|
|
|
# missing deadline -> mark as 'empty' (1) and None for tie |
|
|
|
|
|
return (1, None) |
|
|
|
|
|
try: |
|
|
|
|
|
dt = datetime.fromisoformat(d) |
|
|
|
|
|
return (0, dt) |
|
|
|
|
|
except Exception: |
|
|
|
|
|
# fallback to string compare (ISO-like strings sort correctly) |
|
|
|
|
|
return (0, d) |
|
|
|
|
|
rows.sort(key=_deadline_key, reverse=not self.order_asc) |
|
|
else: |
|
|
else: |
|
|
rows.sort(key=lambda r: r.get(col) or "", reverse=not self.order_asc) |
|
|
rows.sort(key=lambda r: r.get(col) or "", reverse=not self.order_asc) |
|
|
|
|
|
|
|
|
@ -801,6 +857,8 @@ class TaskManagerApp(ctk.CTk): |
|
|
row.append(r.get("last_processed") or "") |
|
|
row.append(r.get("last_processed") or "") |
|
|
elif key == "start_date": |
|
|
elif key == "start_date": |
|
|
row.append(r.get("start_date") or "") |
|
|
row.append(r.get("start_date") or "") |
|
|
|
|
|
elif key == "deadline": |
|
|
|
|
|
row.append(r.get("deadline") or "") |
|
|
elif key == "links_count": |
|
|
elif key == "links_count": |
|
|
row.append(str(links_count)) |
|
|
row.append(str(links_count)) |
|
|
elif key == "notes": |
|
|
elif key == "notes": |
|
|
@ -857,6 +915,7 @@ class TaskManagerApp(ctk.CTk): |
|
|
for r_idx, tid in enumerate(self.displayed_sids): |
|
|
for r_idx, tid in enumerate(self.displayed_sids): |
|
|
task = self.db.get_task(tid) |
|
|
task = self.db.get_task(tid) |
|
|
pr = task.get("priority"); ty = task.get("type"); st = task.get("status") |
|
|
pr = task.get("priority"); ty = task.get("type"); st = task.get("status") |
|
|
|
|
|
ddl = task.get("deadline") |
|
|
# check if processed today using processed_map |
|
|
# check if processed today using processed_map |
|
|
processed = processed_map.get(tid, False) |
|
|
processed = processed_map.get(tid, False) |
|
|
if pr and pr in PRIORITY_DISPLAY: |
|
|
if pr and pr in PRIORITY_DISPLAY: |
|
|
@ -877,6 +936,27 @@ class TaskManagerApp(ctk.CTk): |
|
|
except Exception: |
|
|
except Exception: |
|
|
try: self.sheet.set_cell_bg(r_idx,COL_STATUS,bg) |
|
|
try: self.sheet.set_cell_bg(r_idx,COL_STATUS,bg) |
|
|
except Exception: pass |
|
|
except Exception: pass |
|
|
|
|
|
if ddl: |
|
|
|
|
|
try: |
|
|
|
|
|
dt_deadline = datetime.fromisoformat(ddl) |
|
|
|
|
|
days_to_deadline = (dt_deadline.date() - datetime.now(dt_deadline.tzinfo or timezone.utc).date()).days |
|
|
|
|
|
for (d, c) in DEADLINE_COLOR: |
|
|
|
|
|
if days_to_deadline <= d: |
|
|
|
|
|
bg = c |
|
|
|
|
|
try: self.sheet.highlight_cells(row=r_idx, column=COL_DEADLINE, bg=bg) |
|
|
|
|
|
except Exception: |
|
|
|
|
|
try: self.sheet.set_cell_bg(r_idx,COL_DEADLINE,bg) |
|
|
|
|
|
except Exception: pass |
|
|
|
|
|
break |
|
|
|
|
|
except Exception: |
|
|
|
|
|
pass |
|
|
|
|
|
else: |
|
|
|
|
|
# 取最后一个颜色 |
|
|
|
|
|
bg = DEADLINE_COLOR[-1][1] |
|
|
|
|
|
try: self.sheet.highlight_cells(row=r_idx, column=COL_DEADLINE, bg=bg) |
|
|
|
|
|
except Exception: |
|
|
|
|
|
try: self.sheet.set_cell_bg(r_idx,COL_DEADLINE,bg) |
|
|
|
|
|
except Exception: pass |
|
|
# processed today column: use COL_PROCESSED_TODAY constant |
|
|
# processed today column: use COL_PROCESSED_TODAY constant |
|
|
if processed: |
|
|
if processed: |
|
|
bg = PROCESSED_DISPLAY["yes"]["bg"] |
|
|
bg = PROCESSED_DISPLAY["yes"]["bg"] |
|
|
|