Changeset 9b1abd2 in flexograder
- Timestamp:
- 12/16/25 16:28:34 (4 months ago)
- Branches:
- fake-data, main, master
- Children:
- 1a60982
- Parents:
- a14c6b0
- Files:
-
- 6 edited
-
builder/exam_elements.py (modified) (3 diffs)
-
builder/media_items.py (modified) (7 diffs)
-
gui/attach_media_dialog.py (modified) (4 diffs)
-
gui/detail_panel.py (modified) (7 diffs)
-
gui/option_question_editor.py (modified) (3 diffs)
-
scripts/import3.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
builder/exam_elements.py
ra14c6b0 r9b1abd2 97 97 super()._deserialize_content(content) 98 98 self.topic = content.get("topic", "") 99 self.subtopic = content.get("subtopic", "") 99 100 self.text = content.get("text", "") 100 101 self.media = [media_factory(m) for m in content.get("media", [])] … … 103 104 base = super()._serialize_content() 104 105 base["topic"] = self.topic 106 base["subtopic"] = self.subtopic 105 107 base["text"] = self.text 106 108 base["media"] = [m.to_dict() for m in self.media] … … 111 113 112 114 def get_media(self): 113 return [item for item in self.media]115 return list(self.media) 114 116 115 117 -
builder/media_items.py
ra14c6b0 r9b1abd2 36 36 37 37 @property 38 def type(self) -> str:38 def mtype(self) -> str: 39 39 # logical type used for JSON, defaults to mtype 40 40 return self.mtype … … 57 57 "title": self.title, 58 58 "caption": self.caption, 59 " type": self.type,59 "mtype": self.mtype, 60 60 }) 61 61 return base … … 86 86 87 87 @property 88 def type(self):88 def mtype(self): 89 89 return "image" 90 90 … … 95 95 class AudioItem(MediaItem): 96 96 @property 97 def type(self):97 def mtype(self): 98 98 return "audio" 99 99 … … 104 104 class VideoItem(MediaItem): 105 105 @property 106 def type(self):106 def mtype(self): 107 107 return "video" 108 108 … … 140 140 141 141 @property 142 def type(self):142 def mtype(self): 143 143 return "none" 144 144 … … 147 147 148 148 def media_factory(m: dict) -> MediaItem: 149 print("Dict:", m)150 149 mtype = m.get("mtype", "").lower() 151 150 -
gui/attach_media_dialog.py
ra14c6b0 r9b1abd2 5 5 6 6 class AttachMediaDialog(tk.Toplevel): 7 def __init__(self, parent, question_dict):7 def __init__(self, parent, media_list): 8 8 super().__init__(parent) 9 9 self.title("Media attachments") 10 self. question_dict = question_dict10 self.media_list = media_list 11 11 self.result = [] 12 12 # Ensure media list exists 13 self.media_list = question_dict.get("media", [])14 13 self.resizable(False, False) 15 14 … … 19 18 20 19 for m in self.media_list: 20 print(m) 21 21 mtype = str(m.get("mtype", "unknown")).upper() 22 22 src = m.get("src", "?") … … 52 52 media_entry = {"mtype": mtype, "src": path} 53 53 self.media_list.append(media_entry) 54 print("List:", self.media_list)55 54 self.listbox.insert(tk.END, f"{mtype.upper()}: {path}") 56 55 … … 68 67 def on_ok(self): 69 68 """Return the final edited media list.""" 70 print("OK:", self.media_list)71 69 self.result = self.media_list 72 70 self.destroy() -
gui/detail_panel.py
ra14c6b0 r9b1abd2 2 2 from tkinter import ttk 3 3 import tkinter.font as tkfont 4 from PIL import Image, ImageTk4 # from PIL import Image, ImageTk 5 5 6 6 class DetailPanel(ttk.Frame): … … 27 27 self.font_question = tkfont.Font(family="TkDefaultFont", size=13) 28 28 self.txt_question.configure(font=self.font_question) 29 30 # ─────────────────────────── Topic / Subtopic ───────────────────────── 31 topic_frame = ttk.Frame(self) 32 topic_frame.grid(row=2, column=0, sticky="ew", pady=(0, 4)) 33 topic_frame.columnconfigure(1, weight=1) 34 35 ttk.Label(topic_frame, text="Topic:", foreground="#555").grid( 36 row=0, column=0, sticky="w" 37 ) 38 39 self.lbl_topic = ttk.Label( 40 topic_frame, 41 text="—", 42 foreground="#555" 43 ) 44 self.lbl_topic.grid(row=0, column=1, sticky="w") 45 29 46 # ─────────────────────────── Metadata ──────────────────────────────── 30 47 meta = ttk.LabelFrame(self, text="Metadata", padding=(6, 4)) 31 meta.grid(row= 2, column=0, sticky="ew", pady=(0, 4))48 meta.grid(row=3, column=0, sticky="ew", pady=(0, 4)) 32 49 meta.columnconfigure(1, weight=1) 33 50 34 ttk.Label(meta, text="Type:").grid(row= 0, column=0, sticky="w")51 ttk.Label(meta, text="Type:").grid(row=4, column=0, sticky="w") 35 52 self.lbl_type = ttk.Label(meta, text="—") 36 53 self.lbl_type.grid(row=0, column=1, sticky="w") … … 52 69 self.img_frame.grid(row=3, column=0, sticky="nsew", pady=(0, 4)) 53 70 self.img_frame.columnconfigure(0, weight=1) 54 self.rowconfigure(3, weight=3) # allow preview area to expand55 71 56 self.img_label = ttk.Label(self.img_frame, anchor="center") 72 self.img_frame.rowconfigure(0, weight=1) 73 self.img_frame.columnconfigure(0, weight=1) 74 75 self.img_label = ttk.Label( 76 self.img_frame, 77 anchor="center", 78 relief="sunken" 79 ) 57 80 self.img_label.grid(row=0, column=0, sticky="nsew") 58 81 … … 86 109 """Load an image from disk and resize to fit preview area.""" 87 110 try: 88 img = Image.open(img_path)111 img = tk.PhotoImage(file=img_path) 89 112 except Exception: 90 113 return None 91 114 92 img.thumbnail((self.MAX_IMG_WIDTH, self.MAX_IMG_HEIGHT), Image.Resampling.LANCZOS) 93 return ImageTk.PhotoImage(img) 115 w = img.width() 116 h = img.height() 117 118 # Compute integer subsample factor 119 scale_w = max(1, w // self.MAX_IMG_WIDTH) 120 scale_h = max(1, h // self.MAX_IMG_HEIGHT) 121 scale = max(scale_w, scale_h) 122 123 if scale > 1: 124 img = img.subsample(scale, scale) 125 126 return img 94 127 95 128 # ─────────────────────────────────────────────────────────────────────── … … 101 134 102 135 # Clear image preview 103 self.img_label.configure(image="") 136 104 137 self._image_ref = None 138 self.img_label.configure(image=None) 105 139 106 140 # Clear answers … … 111 145 # Question text 112 146 self.txt_question.insert("1.0", question.text) 147 148 topic = getattr(question, "topic", None) 149 subtopic = getattr(question, "subtopic", None) 150 151 if topic and subtopic: 152 self.lbl_topic.config(text=f"{topic} › {subtopic}") 153 elif topic: 154 self.lbl_topic.config(text=topic) 155 else: 156 self.lbl_topic.config(text="—") 113 157 114 158 # Metadata … … 134 178 135 179 else: 180 self.lbl_topic.config(text="—") 136 181 self.lbl_type.config(text="—") 137 182 self.lbl_state.config(text="—") -
gui/option_question_editor.py
ra14c6b0 r9b1abd2 111 111 112 112 def open_media_dialog(self): 113 dlg = AttachMediaDialog(self, self. question_dict)113 dlg = AttachMediaDialog(self, self.content.get("media", [])) 114 114 dlg.grab_set() 115 115 self.wait_window(dlg) 116 116 117 117 if dlg.result is not None: 118 # ensure target structure exists119 if "media" not in self.question_dict:120 self.question_dict["media"] = []121 122 118 # overwrite old list 123 119 self.question_dict["media"] = dlg.result … … 135 131 136 132 content = {"text": text, 137 "options": self.content.get("options", {}),133 "options": self.content.get("options", []), 138 134 "media": self.content.get("media", []) 139 135 } … … 144 140 "version": self.meta.get("version", 1), 145 141 "domain_id": self.domain_var.get(), 146 "subtype": self.meta["subtype"]147 142 } 148 143 -
scripts/import3.py
ra14c6b0 r9b1abd2 86 86 "answers": {}, 87 87 "domain": current_domain, 88 "topic": current_topic ,89 "subtopic": current_subtopic ,88 "topic": current_topic["title"] if current_topic is not None else None, 89 "subtopic": current_subtopic["title"] if current_subtopic is not None else None, 90 90 } 91 91 continue … … 110 110 # ---- Print results for debugging ---- 111 111 112 print(domains)113 114 112 for q in questions: 115 113 options = [ … … 118 116 ] 119 117 if not options: 120 print(f" ⚠Warning: {q['qid']} has no answer options — inserting placeholder.")118 print(f"Warning: {q['qid']} has no answer options — inserting placeholder.") 121 119 options = [AnswerOption(id="A", text="(keine Antwortoptionen gefunden)")] 122 120 else: … … 126 124 text=q["text"], 127 125 topic=q["topic"], 128 subtopic= ["subtopic"],126 subtopic=q["subtopic"], 129 127 options=options) 130 128 question_catalog.add_questions([current])
Note:
See TracChangeset
for help on using the changeset viewer.
