114 lines
3.9 KiB
Python
114 lines
3.9 KiB
Python
![]() |
import argparse
|
|||
|
import os
|
|||
|
from rich.console import Console
|
|||
|
from rich.markdown import Markdown
|
|||
|
from models import Problem
|
|||
|
|
|||
|
console = Console()
|
|||
|
|
|||
|
def parse_args():
|
|||
|
try:
|
|||
|
parser = argparse.ArgumentParser(description="Luogu CLI 工具")
|
|||
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|||
|
|
|||
|
# lgcli parse PID
|
|||
|
parse_parser = subparsers.add_parser("parse", help="下载指定题目的题面、样例和题解")
|
|||
|
parse_parser.add_argument("pid", help="题目编号,例如 P1145")
|
|||
|
|
|||
|
# lgcli view PID
|
|||
|
view_parser = subparsers.add_parser("view", help="查看题目描述")
|
|||
|
view_parser.add_argument("pid", help="题目编号,例如 P1145")
|
|||
|
|
|||
|
# lgcli solution PID
|
|||
|
solution_parser = subparsers.add_parser("solution", help="查看题解列表或具体题解内容")
|
|||
|
solution_parser.add_argument("pid", help="题目编号,例如 P1145")
|
|||
|
solution_parser.add_argument("--id", help="指定题解文件名(不带.md)", default=None)
|
|||
|
|
|||
|
return parser.parse_args()
|
|||
|
except argparse.ArgumentError as e:
|
|||
|
console.print(f"[red]参数解析错误:{e}[/red]")
|
|||
|
exit(1)
|
|||
|
except Exception as e:
|
|||
|
console.print(f"[red]未知错误:{e}[/red]")
|
|||
|
exit(1)
|
|||
|
|
|||
|
|
|||
|
def cmd_view(pid):
|
|||
|
filename = f"{pid}/{pid}.md"
|
|||
|
if not os.path.exists(filename):
|
|||
|
console.print(f"[red]错误:{pid} 尚未解析,请先运行 lgcli parse {pid}[/red]")
|
|||
|
return
|
|||
|
|
|||
|
try:
|
|||
|
console.clear()
|
|||
|
with open(filename, "r", encoding="utf8") as f:
|
|||
|
content = f.read()
|
|||
|
console.print(Markdown(content))
|
|||
|
except IOError as e:
|
|||
|
console.print(f"[red]读取文件失败:{e}[/red]")
|
|||
|
except Exception as e:
|
|||
|
console.print(f"[red]渲染Markdown失败:{e}[/red]")
|
|||
|
|
|||
|
|
|||
|
def cmd_solution(pid):
|
|||
|
folder = f"{pid}/solutions"
|
|||
|
if not os.path.exists(folder):
|
|||
|
console.print(f"[red]错误:{pid} 题解尚未下载,请先运行 lgcli parse {pid}[/red]")
|
|||
|
return
|
|||
|
|
|||
|
try:
|
|||
|
files = [f for f in os.listdir(folder) if f.endswith(".md")]
|
|||
|
if not files:
|
|||
|
console.print("[yellow]暂无题解。[/yellow]")
|
|||
|
return
|
|||
|
|
|||
|
if args.id is None:
|
|||
|
console.clear()
|
|||
|
console.print("[bold blue]可用题解:[/bold blue]")
|
|||
|
for i, fname in enumerate(files):
|
|||
|
console.print(f"{i+1}. {fname[:-3]}")
|
|||
|
return
|
|||
|
|
|||
|
selected_file = f"{args.id}.md"
|
|||
|
full_path = os.path.join(folder, selected_file)
|
|||
|
if not os.path.exists(full_path):
|
|||
|
console.print(f"[red]错误:题解 {selected_file} 不存在。请检查名称。[/red]")
|
|||
|
return
|
|||
|
|
|||
|
console.clear()
|
|||
|
with open(full_path, "r", encoding="utf8") as f:
|
|||
|
content = f.read()
|
|||
|
console.print(Markdown(content))
|
|||
|
except IOError as e:
|
|||
|
console.print(f"[red]读取题解文件失败:{e}[/red]")
|
|||
|
except Exception as e:
|
|||
|
console.print(f"[red]渲染Markdown失败:{e}[/red]")
|
|||
|
|
|||
|
|
|||
|
def cmd_parse(pid):
|
|||
|
try:
|
|||
|
p = Problem(pid)
|
|||
|
p.gen_all()
|
|||
|
console.print(f"[green]✅ 题目 {pid} 已成功下载到目录 '{pid}'[/green]")
|
|||
|
except ConnectionError as e:
|
|||
|
console.print(f"[red]网络连接错误:{e}[/red]")
|
|||
|
except RuntimeError as e:
|
|||
|
console.print(f"[red]数据解析错误:{e}[/red]")
|
|||
|
except Exception as e:
|
|||
|
console.print(f"[red]未知错误:{e}[/red]")
|
|||
|
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
try:
|
|||
|
args = parse_args()
|
|||
|
|
|||
|
if args.command == "parse":
|
|||
|
cmd_parse(args.pid)
|
|||
|
elif args.command == "view":
|
|||
|
cmd_view(args.pid)
|
|||
|
elif args.command == "solution":
|
|||
|
cmd_solution(args.pid)
|
|||
|
except KeyboardInterrupt:
|
|||
|
console.print("\n[yellow]用户中断操作。[/yellow]")
|
|||
|
except Exception as e:
|
|||
|
console.print(f"[red]发生致命错误:{e}[/red]")
|