sqlzoo Self_join_Quiz 练习题

本文提供了SQLZoo网站上教程测验的解答代码,包括如何查询从Craiglockhart到Haymarket的路线,查找从Haymarket出发可以乘坐一次公交车到达的站点,以及展示从Tollcross出发的服务信息。

https://sqlzoo.net/wiki/Tutorial_Quizzes
https://sqlzoo.net/wiki/Self_join_Quiz

在这里插入图片描述

  1. Select the code that would show it is possible to get from Craiglockhart to Haymarket
SELECT DISTINCT a.name, b.name
  FROM stops a JOIN route z ON a.id=z.stop
  JOIN route y ON y.num = z.num
  JOIN stops b ON y.stop=b.id
 WHERE a.name='Craiglockhart' AND b.name ='Haymarket'
  1. Select the code that shows the stops that are on route.num ‘2A’ which can be reached with one bus from Haymarket
SELECT S2.id, S2.name, R2.company, R2.num
  FROM stops S1, stops S2, route R1, route R2
 WHERE S1.name='Haymarket' AND S1.id=R1.stop
   AND R1.company=R2.company AND R1.num=R2.num
   AND R2.stop=S2.id AND R2.num='2A'
  1. Select the code that shows the services available from Tollcross
SELECT a.company, a.num, stopa.name, stopb.name
  FROM route a JOIN route b ON (a.company=b.company AND a.num=b.num)
  JOIN stops stopa ON (a.stop=stopa.id)
  JOIN stops stopb ON (b.stop=stopb.id)
 WHERE stopa.name='Tollcross'
import json import os import tkinter as tk from tkinter import messagebox, Toplevel, Listbox, Entry, Label, Button, END import sys # ==================== 路径处理函数(关键改进)==================== def resource_path(relative_path): """ 获取打包资源路径(用于只读资源,如 output.jsonl) """ try: base_path = sys._MEIPASS except Exception: base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) def data_path(relative_path): """ 获取用户数据路径(用于可写文件:错题本、别名等)""" base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.abspath(".") return os.path.join(base_dir, relative_path) # ✅ 使用 resource_path 加载原始题库(打包进 exe) OUTPUT_FILE = resource_path('output.jsonl') # ✅ 使用 data_path 存储用户生成的文件(不会被打包,运行时创建) ALIAS_FILE = data_path('mistakebook_aliases.json') # 数字转中文 def number_to_chinese(n): mapping = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十"] return mapping[n] if n <= 20 else str(n) class FastQuizApp: def __init__(self, root): self.root = root self.root.title("📚 Python 刷题系统") self.root.geometry("800x600") self.root.resizable(False, False) self.BASE_MISTAKE_FILE = "mistakes_{}.jsonl" self.MAX_WIDTH = 60 self.load_aliases() # 加载别名 self.show_main_menu() def load_aliases(self): """加载错题本别名(从 data_path 加载)""" if os.path.exists(ALIAS_FILE): try: with open(ALIAS_FILE, 'r', encoding='utf-8') as f: self.aliases = json.load(f) except Exception as e: print(f"⚠️ 加载别名失败:{e}") self.aliases = {} else: self.aliases = {} def save_aliases(self): """保存别名到 data_path""" try: with open(ALIAS_FILE, 'w', encoding='utf-8') as f: json.dump(self.aliases, f, ensure_ascii=False, indent=2) except Exception as e: print(f"⚠️ 无法保存别名:{e}") def get_display_name(self, filename): """获取错题本显示名(优先使用别名)""" if filename in self.aliases: return self.aliases[filename] try: num = int(filename.split('_')[1].split('.')[0]) return f"错题本{number_to_chinese(num)}" except: return filename def show_main_menu(self): self.clear_window() title = tk.Label( self.root, text="风山刷题", font=("Arial", 18, "bold"), pady=50 ) title.pack() btn_style = { "font": ("Arial", 14), "width": 20, "height": 2, "bg": "#4CAF50", "fg": "white", "relief": "flat", "bd": 0 } tk.Button( self.root, text="🔢 顺序答题", command=lambda: self.start_quiz(shuffle=False), **btn_style ).pack(pady=15) tk.Button( self.root, text="🔀 乱序答题", command=lambda: self.start_quiz(shuffle=True), **btn_style ).pack(pady=15) tk.Button( self.root, text="📕 错题本", command=self.select_mistake_file, **btn_style ).pack(pady=15) # 🔔 添加右下角签名 signature = tk.Label( self.root, text="Powered by Python • 风山赋词", font=("Arial", 9), fg="gray", bg="lightgray" if self.root.cget('bg') == 'systemButtonFace' else self.root.cget('bg'), padx=10, pady=2 ) signature.place(relx=1.0, rely=1.0, anchor='se', x=-10, y=-10) def get_next_mistake_filename(self): i = 1 while True: filename = data_path(self.BASE_MISTAKE_FILE.format(i)) if not os.path.exists(filename): return filename i += 1 def compute_question_hash(self, question_data): key = f"{question_data['question']}|" \ f"{sorted(question_data['options'].items())}|" \ f"{question_data['answer']}" return hash(key) def ensure_mistake_file(self): """懒加载错题本文件,保存到 data_path""" if not hasattr(self, '_current_mistake_file'): filename = self.get_next_mistake_filename() try: with open(filename, 'w', encoding='utf-8') as f: pass self._current_练习_答题脚本_mistake_file = filename print(f"📝 新建错题本:{filename}") except Exception as e: messagebox.showerror("错误", f"无法创建错题本文件:\n{e}") raise return self._current_mistake_file def save_mistake_unique(self, question): q_hash = self.compute_question_hash(question) existing_hashes = set() current_file = self.ensure_mistake_file() try: with open(current_file, 'r', encoding='utf-8') as f: for line in f: if line.strip(): q = json.loads(line.strip()) existing_hashes.add(self.compute_question_hash(q)) except Exception as e: print(f"⚠️ 读取错题文件失败:{e}") if q_hash not in existing_hashes: try: with open(current_file, 'a', encoding='utf-8') as f: f.write(json.dumps(question, ensure_ascii=False) + '\n') except Exception as e: print(f"⚠️ 无法保存错题:{e}") def select_mistake_file(self): files = [f for f in os.listdir(data_path(".")) if f.startswith('mistakes_') and f.endswith('.jsonl')] if not files: messagebox.showinfo("提示", "暂无任何错题本可供复习。") return sel_win = Toplevel(self.root) sel_win.title("选择错题本") sel_win.geometry("450x400") sel_win.resizable(False, False) main_frame = tk.Frame(sel_win) main_frame.pack(fill="both", expand=True, padx=10, pady=10) main_frame.grid_rowconfigure(1, weight=1) main_frame.grid_columnconfigure(0, weight=1) top_bar = tk.Frame(main_frame) top_bar.grid(row=0, column=0, sticky="ew", pady=(0, 10)) top_bar.grid_columnconfigure(0, weight=1) top_bar.grid_columnconfigure(1, weight=0) tk.Label(top_bar, text="请选择要复习的错题本:", font=("Arial", 12, "bold")).grid(row=0, column=0, sticky="w") manage_btn = tk.Button( top_bar, text="⚙️ 管理", font=("Arial", 10), bg="gray", fg="white", width=8, height=1 ) manage_btn.grid(row=0, column=1, sticky="e") listbox = Listbox( main_frame, width=50, height=12, font=("Arial", 11), bd=0, highlightthickness=0 ) listbox.grid(row=1, column=0, sticky="nsew", pady=(0, 10)) sorted_files = sorted(files, key=lambda x: int(x.split('_')[1].split('.')[0]) if '_' in x else 0) display_map = {} current_row = listbox.grid_info()['row'] + 1 for i, f in enumerate(sorted_files): display_name = self.get_display_name(f) listbox.insert(END, display_name) display_map[display_name] = f if i < len(sorted_files) - 1: sep = tk.Frame(main_frame, height=1, bg="lightgray") sep.grid(row=current_row, column=0, sticky="ew", padx=30, pady=2) current_row += 1 def refresh_listbox(): listbox.delete(0, END) display_map.clear() for slave in main_frame.grid_slaves(): if isinstance(slave, tk.Frame) and slave.cget('height') == 1: slave.destroy() current_files = [f for f in os.listdir(data_path(".")) if f.startswith('mistakes_') and f.endswith('.jsonl')] if not current_files: messagebox.showinfo("提示", "所有错题本已被删除。") sel_win.destroy() return sorted_current = sorted(current_files, key=lambda x: int(x.split('_')[1].split('.')[0])) new_row = listbox.grid_info()['row'] + 1 for i, f in enumerate(sorted_current): display_name = self.get_display_name(f) listbox.insert(END, display_name) display_map[display_name] = f if i < len(sorted_current) - 1: sep = tk.Frame(main_frame, height=1, bg="lightgray") sep.grid(row=new_row, column=0, sticky="ew", padx=30, pady=2) new_row += 1 manage_btn.config(command=lambda: self.manage_mistakebooks(refresh_listbox, sel_win)) btn_frame = tk.Frame(main_frame) btn_frame.grid(row=999, column=0, pady=10) def on_confirm(): selection = listbox.curselection() if not selection: messagebox.showwarning("提示", "请先选择一个错题本") return selected_name = listbox.get(selection[0]) filename = display_map[selected_name] full_path = data_path(filename) sel_win.destroy() self.review_mistakes_from_file(full_path) def on_cancel(): sel_win.destroy() Button(btn_frame, text="✅ 确定", font=("Arial", 10), bg="blue", fg="white", width=10, command=on_confirm).pack( side="left", padx=10) Button(btn_frame, text="❌ 取消", font=("Arial", 10), bg="gray", fg="white", width=10, command=on_cancel).pack( side="left", padx=10) sel_win.protocol("WM_DELETE_WINDOW", on_cancel) def manage_mistakebooks(self, refresh_parent, parent_window=None): for w in self.root.winfo_children(): if isinstance(w, Toplevel) and w.title() == "管理错题本": w.lift() return manage_win = Toplevel(self.root) manage_win.title("管理错题本") manage_win.geometry("500x400") manage_win.resizable(False, False) tk.Label(manage_win, text="管理错题本(可重命名或删除)", font=("Arial", 12, "bold")).pack(pady=10) container = tk.Frame(manage_win) container.pack(fill="both", expand=True, padx=20, pady=10) canvas = tk.Canvas(container) scrollbar = tk.Scrollbar(container, orient="vertical", command=canvas.yview) scrollable_frame = tk.Frame(canvas) scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))) canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") canvas.configure(yscrollcommand=scrollbar.set) items = [] def refresh_local(): nonlocal items for item in items: for widget in item.values(): if isinstance(widget, (list, tuple)): for w in widget: try: w.destroy() except tk.TclError: pass else: try: widget.destroy() except tk.TclError: pass items.clear() current_files = [f for f in os.listdir(data_path(".")) if f.startswith('mistakes_') and f.endswith('.jsonl')] sorted_current = sorted(current_files, key=lambda x: int(x.split('_')[1].split('.')[0])) if not sorted_current: tk.Label(scrollable_frame, text="暂无错题本", fg="gray").pack(pady=20) return for f in sorted_current: frame = tk.Frame(scrollable_frame, relief="solid", bd=1, padx=10, pady=8) frame.pack(fill="x", pady=5) alias_var = tk.StringVar(value=self.aliases.get(f, self.get_display_name(f))) label = Label(frame, text=f"📁 {f}", font=("Courier", 9), fg="green") label.grid(row=0, column=0, sticky="w", padx=5) entry = Entry(frame, textvariable=alias_var, width=20, font=("Arial", 10)) entry.grid(row=0, column=1, padx=5) def save_name(fn=f, var=alias_var): new_name = var.get().strip() if new_name: self.aliases[fn] = new_name self.save_aliases() messagebox.showinfo("成功", f"已将 {fn} 重命名为:{new_name}") else: messagebox.showwarning("无效", "名称不能为空") save_btn = Button(frame, text="💾 保存", font=("Arial", 8), bg="orange", fg="white", command=save_name) save_btn.grid(row=0, column=2, padx=5) def delete_this_file(filename): if messagebox.askyesno("确认删除", f"确定要删除错题本 '{filename}' 吗?\n此操作不可恢复!"): try: os.remove(data_path(filename)) if filename in self.aliases: del self.aliases[filename] self.save_aliases() refresh_parent() refresh_local() messagebox.showinfo("删除成功", f"已删除 {filename}") except Exception as e: messagebox.showerror("错误", f"删除失败:{str(e)}") del_btn = Button(frame, text="🗑️ 删除", font=("Arial", 8), bg="red", fg="white", command=lambda fn=f: delete_this_file(fn)) del_btn.grid(row=0, column=3, padx=5) items.append({ 'frame': frame, 'label': label, 'entry': entry, 'save_btn': save_btn, 'del_btn': del_btn, 'var': alias_var }) refresh_local() canvas.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") close_btn = Button(manage_win, text="关闭", bg="blue", fg="white", command=manage_win.destroy) close_btn.pack(pady=10) def on_close(): nonlocal items items.clear() try: manage_win.destroy() except: pass manage_win.protocol("WM_DELETE_WINDOW", on_close) def review_mistakes_from_file(self, file_path): questions = self.load_questions(file_path) if not questions: messagebox.showinfo("提示", f"该错题本中没有有效题目。") return basename = os.path.basename(file_path) self.current_mistake_file = file_path self.run_practice_mode(questions, title=f"【错题本】{self.get_display_name(basename)}", mode="review") def start_quiz(self, shuffle=False): if hasattr(self, '_current_mistake_file'): delattr(self, '_current_mistake_file') questions = self.load_questions(OUTPUT_FILE) if not questions: messagebox.showwarning("提示", "未找到任何题目,请检查 output.jsonl 文件。") return if shuffle: import random random.shuffle(questions) self.run_practice_mode(questions, title="【随机刷题】" if shuffle else "【顺序练习】", mode="quiz") def load_questions(self, file_path): if not os.path.exists(file_path): return [] questions = [] try: with open(file_path, 'r', encoding='utf-8') as f: for line in f: if line.strip(): q = json.loads(line.strip()) if q.get('question') and q.get('options') and len(q.get('answer', '')) == 1: questions.append(q) except Exception as e: messagebox.showerror("错误", f"读取题目失败:{e}") return questions def wrap_text(self, text, max_len): lines = [] while len(text) > max_len: split_point = text.rfind(' ', 0, max_len) if split_point == -1: split_point = max_len lines.append(text[:split_point]) text = text[split_point:].strip() lines.append(text) return lines def format_option(self, key, value): first_line = f"{key}. {value}" if len(first_line) <= self.MAX_WIDTH: return first_line lines = self.wrap_text(first_line, self.MAX_WIDTH) indent = f" {' ' * len(key)} " wrapped_rest = "\n".join(indent + part for part in self.wrap_text(lines[1], self.MAX_WIDTH - len(indent))) return lines[0] + "\n" + wrapped_rest def run_practice_mode(self, questions, title, mode): self.clear_window() self.mode = mode self.current_index = 0 self.questions = questions self.title_text = title tk.Label(self.root, text=title, font=("Arial", 16, "bold"), fg="blue").pack(pady=10) self.question_label = tk.Label( self.root, text="", wraplength=700, justify="left", anchor="w", font=("Arial", 14, "bold"), padx=20 ) self.question_label.pack(pady=20, padx=20, fill="x") self.buttons_frame = tk.Frame(self.root) self.buttons_frame.pack(pady=10, padx=20, fill="both", expand=True) self.progress_label = tk.Label( self.root, text="", fg="gray", font=("Arial", 10) ) self.progress_label.pack(side="bottom", anchor="e", padx=20, pady=30) self.submit_button = tk.Button( self.root, text="📤 交卷", font=("Arial", 10), bg="orange", fg="white", command=self.submit_exam ) self.submit_button.place(relx=0.9, rely=0.9, anchor="center") self.show_question() def submit_exam(self): if messagebox.askyesno("确认交卷?", "确定要提前结束并返回主菜单吗?"): self.show_main_menu() def normalize_answer(self, answer: str) -> str: return answer.strip().upper()[0] def show_question(self): self.progress_label.config(text=f"第 {self.current_index + 1} / {len(self.questions)} 题") q = self.questions[self.current_index] correct_answer = self.normalize_answer(q['answer']) wrapped_question = "\n".join(self.wrap_text(q['question'], self.MAX_WIDTH + 10)) self.question_label.config(text=wrapped_question) for widget in self.buttons_frame.winfo_children(): widget.destroy() for opt_key in sorted(q['options'].keys()): full_text = self.format_option(opt_key, q['options'][opt_key]) btn = tk.Button( self.buttons_frame, text=full_text, font=("Arial", 12), width=70, anchor="w", justify="left", padx=10, pady=8, bg="#f9f9f9", relief="solid", bd=1, wraplength=750 ) btn.pack(fill="x", pady=4) def on_click(opt=opt_key, button=btn, q=q): self.on_option_click(opt, button, correct_answer, q) btn.bind("<Button-1>", lambda e, fn=on_click: fn()) def on_option_click(self, selected, button, correct_answer, question): is_correct = self.normalize_answer(selected) == correct_answer if is_correct: button.config(bg="lightgreen", fg="black") self.question_label.config(text="✅ 正确!") else: button.config(bg="lightcoral", fg="black") self.question_label.config(text=f"❌ 错误!正确答案:{correct_answer}") for widget in self.buttons_frame.winfo_children(): widget.unbind("<Button-1>") if self.mode == "quiz" and not is_correct: self.save_mistake_unique(question) elif self.mode == "review" and is_correct: self.remove_question_from_file(question) self.root.after(500, self.next_question) def remove_question_from_file(self, question_to_remove): filename = getattr(self, 'current_mistake_file', None) if not filename or not os.path.exists(filename): return remaining = [] q_hash = self.compute_question_hash(question_to_remove) try: with open(filename, 'r', encoding='utf-8') as f: for line in f: if line.strip(): q = json.loads(line.strip()) if self.compute_question_hash(q) != q_hash: remaining.append(q) with open(filename, 'w', encoding='utf-8') as f: for q in remaining: f.write(json.dumps(q, ensure_ascii=False) + '\n') except Exception as e: print(f"⚠️ 删除错题失败:{e}") def next_question(self): self.current_index += 1 if self.current_index < len(self.questions): self.show_question() else: msg = f"🎉 已完成本轮 {len(self.questions)} 道题目!" messagebox.showinfo("完成", msg) self.show_main_menu() def clear_window(self): for widget in self.root.winfo_children(): widget.destroy() if __name__ == '__main__': root = tk.Tk() app = FastQuizApp(root) root.mainloop() 这个脚本只能运行单选题,对多选题没有反应,帮我修改一下。要求:1.在ui页面显示题型(type有“单选题”和“多选题”) 2.要求多选题所有选项都选上才能判断为本题正确 3.其他保持原样
12-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值