import tkinter as tk from tkinter import ttk, messagebox import re import uuid class ReverseMoralithicChainGeneratorMode4: def __init__(self, root): self.root = root self.root.title("Reverse Moralithic Chain Generator - Mode 4") self.valid_statement_source_pairs = { 'ST31': 'SO30', 'ST32': 'SO31', 'ST33': 'SO32', 'ST34': 'SO33', 'ST43': 'SO42', 'ST44': 'SO43', 'ST45': 'SO44', 'ST46': 'SO45', 'ST47': 'SO46', 'ST48': 'SO47', 'ST49': 'SO48', 'ST50': 'SO49', 'ST52': 'SO51', 'ST54': 'SO53', 'ST56': 'SO55', 'ST58': 'SO57', 'ST59': 'SO58', 'ST60': 'SO59', 'ST61': 'SO60', 'ST62': 'SO61', 'ST63': 'SO62', 'ST64': 'SO63', 'ST65': 'SO64', 'ST66': 'SO65', 'ST67': 'SO66', 'ST68': 'SO67', 'ST69': 'SO68', 'ST70': 'SO69', 'ST71': 'SO70', 'ST72': 'SO71', 'ST73': 'SO72', 'ST74': 'SO73' } self.valid_statement_value_pairs = { 'ST31': {'values': ['VL31'], 'required': 'VL31'}, 'ST32': {'values': ['VL31', 'VL32'], 'required': 'VL32'}, 'ST33': {'values': ['VL33'], 'required': 'VL33'}, 'ST34': {'values': ['VL31', 'VL32', 'VL33', 'VL34'], 'required': 'VL34'}, 'ST43': {'values': ['VL43'], 'required': 'VL43'}, 'ST44': {'values': ['VL43', 'VL44'], 'required': 'VL44'}, 'ST45': {'values': ['VL45'], 'required': 'VL45'}, 'ST46': {'values': ['VL43', 'VL44', 'VL45', 'VL46'], 'required': 'VL46'}, 'ST47': {'values': ['VL47'], 'required': 'VL47'}, 'ST48': {'values': ['VL47', 'VL48'], 'required': 'VL48'}, 'ST49': {'values': ['VL47', 'VL48', 'VL49'], 'required': 'VL49'}, 'ST50': {'values': ['VL47', 'VL48', 'VL49', 'VL50'], 'required': 'VL50'}, 'ST52': {'values': ['VL52'], 'required': 'VL52'}, 'ST54': {'values': ['VL54'], 'required': 'VL54'}, 'ST56': {'values': ['VL56'], 'required': 'VL56'}, 'ST58': {'values': ['VL58'], 'required': 'VL58'}, 'ST59': {'values': ['VL59'], 'required': 'VL59'}, 'ST60': {'values': ['VL59', 'VL60'], 'required': 'VL60'}, 'ST61': {'values': ['VL61'], 'required': 'VL61'}, 'ST62': {'values': ['VL59', 'VL60', 'VL61', 'VL62'], 'required': 'VL62'}, 'ST63': {'values': ['VL63'], 'required': 'VL63'}, 'ST64': {'values': ['VL63', 'VL64'], 'required': 'VL64'}, 'ST65': {'values': ['VL65'], 'required': 'VL65'}, 'ST66': {'values': ['VL63', 'VL64', 'VL65', 'VL66'], 'required': 'VL66'}, 'ST67': {'values': ['VL67'], 'required': 'VL67'}, 'ST68': {'values': ['VL67', 'VL68'], 'required': 'VL68'}, 'ST69': {'values': ['VL69'], 'required': 'VL69'}, 'ST70': {'values': ['VL67', 'VL68', 'VL69', 'VL70'], 'required': 'VL70'}, 'ST71': {'values': ['VL71'], 'required': 'VL71'}, 'ST72': {'values': ['VL71', 'VL72'], 'required': 'VL72'}, 'ST73': {'values': ['VL73'], 'required': 'VL73'}, 'ST74': {'values': ['VL71', 'VL72', 'VL73', 'VL74'], 'required': 'VL74'} } self.valid_interaction_tags = ( [f"NI0{i}" for i in range(4, 8)] + [f"EF{i}" for i in range(32, 64)] + [f"RP0{i}" for i in range(4, 8)] + [f"RE{i:02d}" for i in range(8, 16)] + [f"AC{i:02d}" for i in range(8, 16)] ) self.valid_means_tags = ["ME02", "ME03"] self.valid_displayment_tags = ["DP02", "DP03"] self.valid_statement_tags = list(self.valid_statement_source_pairs.keys()) self.valid_source_tags = list(self.valid_statement_source_pairs.values()) self.valid_value_tags = sorted(set(sum([pair['values'] for pair in self.valid_statement_value_pairs.values()], []))) self.value_tags = [] self.conversion_rules = { 'AC08': ['RE08', 'RE12'], 'AC09': ['RE09', 'RE13'], 'AC10': ['RE10', 'RE14'], 'AC11': ['RE11', 'RE15'], 'AC12': ['RE08', 'RE12'], 'AC13': ['RE09', 'RE13'], 'AC14': ['RE10', 'RE14'], 'AC15': ['RE11', 'RE15'], 'RE08': ['AC08', 'AC12'], 'RE09': ['AC09', 'AC13'], 'RE10': ['AC10', 'AC14'], 'RE11': ['AC11', 'AC15'], 'RE12': ['AC08', 'AC12'], 'RE13': ['AC09', 'AC13'], 'RE14': ['AC10', 'AC14'], 'RE15': ['AC11', 'AC15'] } self.transformation_rules = { 'NI04': ['NI06'], 'NI05': ['NI07'], 'NI06': ['NI04'], 'NI07': ['NI05'], 'EF32': ['EF34', 'EF36', 'EF38'], 'EF33': ['EF35', 'EF37', 'EF39'], 'EF34': ['EF32', 'EF36', 'EF38'], 'EF35': ['EF33', 'EF37', 'EF39'], 'EF36': ['EF32', 'EF34', 'EF38'], 'EF37': ['EF33', 'EF35', 'EF39'], 'EF38': ['EF32', 'EF34', 'EF36'], 'EF39': ['EF33', 'EF35', 'EF37'], 'EF40': ['EF42', 'EF44', 'EF46'], 'EF41': ['EF43', 'EF45', 'EF47'], 'EF42': ['EF40', 'EF44', 'EF46'], 'EF43': ['EF41', 'EF45', 'EF47'], 'EF44': ['EF40', 'EF42', 'EF46'], 'EF45': ['EF41', 'EF43', 'EF47'], 'EF46': ['EF40', 'EF42', 'EF44'], 'EF47': ['EF41', 'EF43', 'EF45'], 'EF48': ['EF50', 'EF52', 'EF54'], 'EF49': ['EF51', 'EF53', 'EF55'], 'EF50': ['EF48', 'EF52', 'EF54'], 'EF51': ['EF49', 'EF53', 'EF55'], 'EF52': ['EF48', 'EF50', 'EF54'], 'EF53': ['EF49', 'EF51', 'EF55'], 'EF54': ['EF48', 'EF50', 'EF52'], 'EF55': ['EF49', 'EF51', 'EF53'], 'EF56': ['EF58', 'EF60', 'EF62'], 'EF57': ['EF59', 'EF61', 'EF63'], 'EF58': ['EF56', 'EF60', 'EF62'], 'EF59': ['EF57', 'EF61', 'EF63'], 'EF60': ['EF56', 'EF58', 'EF62'], 'EF61': ['EF57', 'EF59', 'EF63'], 'EF62': ['EF56', 'EF58', 'EF60'], 'EF63': ['EF57', 'EF59', 'EF61'], 'RE08': ['RE12'], 'RE09': ['RE13'], 'RE10': ['RE14'], 'RE11': ['RE15'], 'RE12': ['RE08'], 'RE13': ['RE09'], 'RE14': ['RE10'], 'RE15': ['RE11'], 'AC08': ['AC12'], 'AC09': ['AC13'], 'AC10': ['AC14'], 'AC11': ['AC15'], 'AC12': ['AC08'], 'AC13': ['AC09'], 'AC14': ['AC10'], 'AC15': ['AC11'], 'RP04': [], 'RP05': [], 'RP06': [], 'RP07': [] } # Forward transformation rules self.forward_conversion_rules = {} for current_tag, prev_list in self.conversion_rules.items(): for prev_tag in prev_list: if prev_tag not in self.forward_conversion_rules: self.forward_conversion_rules[prev_tag] = [] self.forward_conversion_rules[prev_tag].append(current_tag) self.forward_transformation_rules = {} for current_tag, prev_list in self.transformation_rules.items(): for prev_tag in prev_list: if prev_tag not in self.forward_transformation_rules: self.forward_transformation_rules[prev_tag] = [] self.forward_transformation_rules[prev_tag].append(current_tag) self.transformations = [] # For storing transformations self.chain_text = "" self.interaction_tag_value = "" # To store the initial interaction tag self.current_tag = "" # To track the latest tag in the transformation chain self.setup_gui() def setup_gui(self): canvas_frame = ttk.Frame(self.root) canvas_frame.pack(fill=tk.BOTH, expand=True) self.canvas = tk.Canvas(canvas_frame) v_scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.VERTICAL, command=self.canvas.yview) h_scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview) self.main_frame = ttk.Frame(self.canvas) self.canvas.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set) self.canvas.create_window((0, 0), window=self.main_frame, anchor="nw") v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10) self.main_frame.bind("", self._update_scrollregion) self.canvas.bind_all("", self._on_mousewheel) self.canvas.bind_all("", self._on_mousewheel) self.canvas.bind_all("", self._on_mousewheel) ttk.Label(self.main_frame, text="1. What is the moral statement in question?").pack(anchor=tk.W) self.statement_text = tk.Text(self.main_frame, height=3, width=50) self.statement_text.pack(anchor=tk.W, pady=2) self.statement_text.tag_configure("values", foreground="red") self.statement_text.tag_configure("parts", foreground="blue") self.statement_text.tag_configure("limits", foreground="green") ttk.Label(self.main_frame, text="Moral Statement Moralithic Tag:").pack(anchor=tk.W) self.statement_tag = ttk.Combobox(self.main_frame, values=self.valid_statement_tags, state="readonly") self.statement_tag.pack(anchor=tk.W, pady=5) self.statement_tag.bind("<>", self.update_value_comboboxes) ttk.Label(self.main_frame, text="2. What are the moral values of the statement in question?").pack(anchor=tk.W) self.values_input = ttk.Entry(self.main_frame, width=50) self.values_input.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Add Moral Values and Moralithic Tags", command=self.add_value_tag).pack(anchor=tk.W, pady=5) self.values_frame = ttk.Frame(self.main_frame) self.values_frame.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Moral Values (for reference):").pack(anchor=tk.W) self.values_text = tk.Text(self.main_frame, height=3, width=50) self.values_text.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Necessary Parts (comma-separated):").pack(anchor=tk.W) self.parts_input = ttk.Entry(self.main_frame, width=50) self.parts_input.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Add Necessary Parts", command=self.add_parts).pack(anchor=tk.W, pady=5) self.parts_frame = ttk.Frame(self.main_frame) self.parts_frame.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Necessary Parts (for reference):").pack(anchor=tk.W) self.parts_text = tk.Text(self.main_frame, height=3, width=50) self.parts_text.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Limitations (optional, comma-separated):").pack(anchor=tk.W) self.limits_input = ttk.Entry(self.main_frame, width=50) self.limits_input.pack(anchor=tk.W, pady=2) ttk.Button(self.main_frame, text="Add Limitations", command=self.add_limits).pack(anchor=tk.W, pady=5) self.limits_frame = ttk.Frame(self.main_frame) self.limits_frame.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Limitations (for reference):").pack(anchor=tk.W) self.limits_text = tk.Text(self.main_frame, height=3, width=50) self.limits_text.pack(anchor=tk.W, pady=5) self.values_text.bind("", self.update_statement_colors) self.parts_text.bind("", self.update_statement_colors) self.limits_text.bind("", self.update_statement_colors) ttk.Label(self.main_frame, text="3. What is the source of the statement?").pack(anchor=tk.W) self.source_text = ttk.Entry(self.main_frame, width=50) self.source_text.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Source Moralithic Tag:").pack(anchor=tk.W) self.source_tag = ttk.Combobox(self.main_frame, values=self.valid_source_tags, state="readonly") self.source_tag.pack(anchor=tk.W, pady=5) ttk.Label(self.main_frame, text="4. What displayment was used to source the statement?").pack(anchor=tk.W) self.displayment_text = ttk.Entry(self.main_frame, width=50) self.displayment_text.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Displayment Moralithic Tag:").pack(anchor=tk.W) self.displayment_tag = ttk.Combobox(self.main_frame, values=self.valid_displayment_tags, state="readonly") self.displayment_tag.pack(anchor=tk.W, pady=5) ttk.Label(self.main_frame, text="5. What is the moralithic tag of the interactor/non-interactor?").pack(anchor=tk.W) self.interaction_tag = ttk.Combobox(self.main_frame, values=sorted(self.valid_interaction_tags), state="readonly") self.interaction_tag.pack(anchor=tk.W, pady=2) self.means_frame = ttk.Frame(self.main_frame) ttk.Label(self.means_frame, text="Means (for Enforcers):").pack(anchor=tk.W) self.means_text = ttk.Entry(self.means_frame, width=50) self.means_text.pack(anchor=tk.W, pady=2) ttk.Label(self.means_frame, text="Means Moralithic Tag:").pack(anchor=tk.W) self.means_tag = ttk.Combobox(self.main_frame, values=self.valid_means_tags, state="readonly") self.means_tag.pack(anchor=tk.W, pady=2) self.repeater_displayment_frame = ttk.Frame(self.main_frame) ttk.Label(self.repeater_displayment_frame, text="Displayment (for Repeaters):").pack(anchor=tk.W) self.repeater_displayment_text = ttk.Entry(self.repeater_displayment_frame, width=50) self.repeater_displayment_text.pack(anchor=tk.W, pady=2) ttk.Label(self.repeater_displayment_frame, text="Repeater Displayment Moralithic Tag:").pack(anchor=tk.W) self.repeater_displayment_tag = ttk.Combobox(self.main_frame, values=self.valid_displayment_tags, state="readonly") self.repeater_displayment_tag.pack(anchor=tk.W, pady=2) ttk.Label(self.main_frame, text="Entity Identity:").pack(anchor=tk.W) self.entity_text = ttk.Entry(self.main_frame, width=50) self.entity_text.pack(anchor=tk.W, pady=5) self.interaction_tag.bind("<>", lambda e: [self.update_means_visibility(e), self.update_repeater_displayment_visibility(e)]) self.update_means_visibility() self.update_repeater_displayment_visibility() ttk.Button(self.main_frame, text="Generate Reverse Moralithic Chain", command=self.generate_chain).pack(anchor=tk.W, pady=10) self.result_text = tk.Text(self.main_frame, height=5, width=70) self.result_text.pack(anchor=tk.W, pady=5) self.result_text.tag_configure("values", foreground="red") self.result_text.tag_configure("parts", foreground="blue") self.result_text.tag_configure("limits", foreground="green") self.result_text.tag_configure("bold", font=("TkDefaultFont", 9, "bold")) ttk.Label(self.main_frame, text="Add Footnote to Generated Chain:", font=("TkDefaultFont", 9, "bold")).pack(anchor=tk.W, pady=10) self.footnote_container = ttk.Frame(self.main_frame) self.footnote_container.pack(anchor=tk.W, pady=5, fill=tk.X) ttk.Button(self.main_frame, text="Copy Chain", command=self.copy_chain).pack(anchor=tk.W, pady=5) def update_value_comboboxes(self, event=None): statement_tag = self.statement_tag.get() if statement_tag in self.valid_statement_value_pairs: valid_values = self.valid_statement_value_pairs[statement_tag]['values'] for _, tag in self.value_tags: tag.configure(values=valid_values) if tag.get() not in valid_values: tag.set('') def update_repeater_displayment_visibility(self, event=None): if self.interaction_tag.get().startswith("RP"): self.repeater_displayment_frame.pack(anchor=tk.W, pady=2) self.repeater_displayment_tag.pack(anchor=tk.W, pady=2) else: self.repeater_displayment_frame.pack_forget() self.repeater_displayment_tag.pack_forget() def _update_scrollregion(self, event): self.canvas.configure(scrollregion=self.canvas.bbox("all")) def _on_mousewheel(self, event): if event.delta: self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") elif event.num == 4: self.canvas.yview_scroll(-1, "units") elif event.num == 5: self.canvas.yview_scroll(1, "units") def update_means_visibility(self, event=None): if self.interaction_tag.get().startswith("EF"): self.means_frame.pack(anchor=tk.W, pady=2) self.means_tag.pack(anchor=tk.W, pady=2) else: self.means_frame.pack_forget() self.means_tag.pack_forget() def copy_chain(self): try: chain_text = self.result_text.get("1.0", tk.END).strip() if not chain_text: messagebox.showwarning("Warning", "No chain to copy. Please generate a chain first.") return self.root.clipboard_clear() self.root.clipboard_append(chain_text) self.root.update() messagebox.showinfo("Success", "Chain copied to clipboard!") except Exception as e: messagebox.showerror("Error", f"Failed to copy chain: {str(e)}") def add_value_tag(self): try: values = self.values_input.get().strip() if not values: messagebox.showerror("Error", "Please enter values before adding tags.") return value_list = [v.strip() for v in values.split(",") if v.strip()] if not value_list: messagebox.showerror("Error", "No valid values provided.") return if len(value_list) < 1: messagebox.showerror("Error", "At least one value is required.") return statement_tag = self.statement_tag.get() if not statement_tag: messagebox.showerror("Error", "Please select a statement tag before adding values.") return valid_values = self.valid_statement_value_pairs.get(statement_tag, {'values': [], 'required': None})['values'] required_value = self.valid_statement_value_pairs.get(statement_tag, {'values': [], 'required': None})['required'] for widget in self.values_frame.winfo_children(): widget.destroy() self.value_tags.clear() for i, value in enumerate(value_list, 1): frame = ttk.Frame(self.values_frame) frame.pack(anchor=tk.W, pady=2) ttk.Label(frame, text=f"Value {i}: {value}").pack(side=tk.LEFT) tag = ttk.Combobox(frame, values=valid_values, state="readonly", width=10) tag.pack(side=tk.LEFT, padx=5) self.value_tags.append((value, tag)) self.values_text.delete("1.0", tk.END) self.values_text.insert("1.0", ", ".join(f"{i}. {v}" for i, v in enumerate(value_list, 1))) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.update_statement_colors() except Exception as e: messagebox.showerror("Error", f"Failed to add moral values: {str(e)}") def add_parts(self): try: parts = self.parts_input.get().strip() if not parts: messagebox.showerror("Error", "Please enter necessary parts before adding.") return parts_list = [p.strip() for p in parts.split(",") if p.strip()] if not parts_list: messagebox.showerror("Error", "No valid necessary parts provided.") return for widget in self.parts_frame.winfo_children(): widget.destroy() for i, part in enumerate(parts_list, 1): frame = ttk.Frame(self.parts_frame) frame.pack(anchor=tk.W, pady=2) ttk.Label(frame, text=f"Part {i}: {part}").pack(side=tk.LEFT) self.parts_text.delete("1.0", tk.END) self.parts_text.insert("1.0", ", ".join(f"{i}. {p}" for i, p in enumerate(parts_list, 1))) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.update_statement_colors() except Exception as e: messagebox.showerror("Error", f"Failed to add parts: {str(e)}") def add_limits(self): try: limits = self.limits_input.get().strip() if not limits: return limits_list = [l.strip() for l in limits.split(",") if l.strip()] if not limits_list: return for widget in self.limits_frame.winfo_children(): widget.destroy() for i, limit in enumerate(limits_list, 1): frame = ttk.Frame(self.limits_frame) frame.pack(anchor=tk.W, pady=2) ttk.Label(frame, text=f"Limitation {i}: {limit}").pack(side=tk.LEFT) self.limits_text.delete("1.0", tk.END) self.limits_text.insert("1.0", ", ".join(f"{i}. {l}" for i, l in enumerate(limits_list, 1))) self.canvas.configure(scrollregion=self.canvas.bbox("all")) self.update_statement_colors() except Exception as e: messagebox.showerror("Error", f"Failed to add limitations: {str(e)}") def update_statement_colors(self, event=None): try: statement = self.statement_text.get("1.0", tk.END).strip() values = self.values_text.get("1.0", tk.END).strip() parts = self.parts_text.get("1.0", tk.END).strip() limits = self.limits_text.get("1.0", tk.END).strip() self.statement_text.tag_remove("values", "1.0", tk.END) self.statement_text.tag_remove("parts", "1.0", tk.END) self.statement_text.tag_remove("limits", "1.0", tk.END) value_list = [v.strip() for v in values.split(",") if v.strip()] value_list = [re.sub(r'^\s*[0-9]+\.\s*', '', v).strip() for v in value_list] parts_list = [p.strip() for p in parts.split(",") if p.strip()] parts_list = [re.sub(r'^\s*[0-9]+\.\s*', '', p).strip() for p in parts_list] limits_list = [l.strip() for l in limits.split(",") if l.strip()] limits_list = [re.sub(r'^\s*[0-9]+\.\s*', '', l).strip() for l in limits_list] components = [(v, "values") for v in value_list] + [(p, "parts") for p in parts_list] + [(l, "limits") for l in limits_list] component_positions = [] for comp, tag in components: start = statement.find(comp) if start != -1: component_positions.append((start, start + len(comp), comp, tag)) component_positions.sort() for start, end, comp, tag in component_positions: self.statement_text.tag_add(tag, f"1.0+{start}c", f"1.0+{start + len(comp)}c") return component_positions except Exception as e: self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error in coloring components: {str(e)}") self.result_text.configure(state="disabled") return [] def validate_chain(self, interaction_tag, displayment_tag, means_tag, statement_tag, source_tag, value_list, parts_list, limits_list, value_tags, repeater_displayment_tag=None): try: if interaction_tag not in self.valid_interaction_tags: return False, f"Invalid interaction tag. Must be one of: {', '.join(self.valid_interaction_tags)}." if interaction_tag.startswith("EF"): if not means_tag: return False, "Means tag is required for Enforcer interaction tags." if means_tag not in self.valid_means_tags: return False, f"Invalid means tag. Must be one of: {', '.join(self.valid_means_tags)}." if interaction_tag.startswith("RP"): if not repeater_displayment_tag: return False, "Displayment tag is required for Repeater interaction tags." if repeater_displayment_tag not in self.valid_displayment_tags: return False, f"Invalid repeater displayment tag. Must be one of: {', '.join(self.valid_displayment_tags)}." if displayment_tag not in self.valid_displayment_tags: return False, "Invalid displayment tag. Only DP02 and DP03 are allowed." if statement_tag not in self.valid_statement_source_pairs: return False, f"Invalid statement tag. Must be one of: {', '.join(self.valid_statement_source_pairs.keys())}." if source_tag not in self.valid_statement_source_pairs.values(): return False, f"Invalid source tag. Must be one of: {', '.join(self.valid_statement_source_pairs.values())}." if self.valid_statement_source_pairs.get(statement_tag) != source_tag: return False, "Incompatible statement and source tags." if len(value_list) < 1: return False, "At least one value is required." if len(parts_list) < 1: return False, "At least one necessary part is required." if len(value_list) != len(value_tags): return False, f"Mismatch between values ({len(value_list)}) and value tags ({len(value_tags)})." missing_tags = [f"Value {i+1}: '{value}'" for i, (value, tag) in enumerate(self.value_tags) if not tag.get()] if missing_tags: return False, f"Missing moralithic tags for the following values:\n" + "\n".join(f"- {mt}" for mt in missing_tags) valid_values = self.valid_statement_value_pairs.get(statement_tag, {'values': [], 'required': None})['values'] required_value = self.valid_statement_value_pairs.get(statement_tag, {'values': [], 'required': None})['required'] for v in value_tags: if v not in valid_values: return False, f"Invalid value tag '{v}' for statement tag '{statement_tag}'. Must be one of: {', '.join(valid_values)}." if required_value and required_value not in value_tags: return False, f"Required value tag '{required_value}' for statement tag '{statement_tag}' is missing." statement = self.statement_text.get("1.0", tk.END).strip() components = [(v, "Value") for v in value_list] + [(p, "Necessary Part") for p in parts_list] + [(l, "Limitation") for l in limits_list] error_msg = [] component_positions = [] for comp, comp_type in components: start = statement.find(comp) if start == -1: error_msg.append(f"{comp_type} '{comp}' not found in statement.") else: component_positions.append((start, start + len(comp), comp, comp_type)) component_positions.sort() current_pos = 0 for i, (start, end, comp, comp_type) in enumerate(component_positions, 1): if start < current_pos: error_msg.append(f"{comp_type} '{comp}' overlaps with previous components at position {start}.") current_pos = max(current_pos, end) if error_msg: error_msg.insert(0, "Component sequence error: Components must appear in order without overlap.") return False, "\n".join(error_msg) return True, "" except Exception as e: return False, f"Validation error: {str(e)}" def generate_chain(self): try: statement = self.statement_text.get("1.0", tk.END).strip() statement_tag = self.statement_tag.get() source = self.source_text.get().strip() source_tag = self.source_tag.get() displayment = self.displayment_text.get().strip() displayment_tag = self.displayment_tag.get() interaction_tag = self.interaction_tag.get() means = self.means_text.get().strip() means_tag = self.means_tag.get() repeater_displayment = self.repeater_displayment_text.get().strip() if hasattr(self, 'repeater_displayment_text') else '' repeater_displayment_tag = self.repeater_displayment_tag.get() if hasattr(self, 'repeater_displayment_tag') else '' entity = self.entity_text.get().strip() if not (statement and statement_tag and source and source_tag and interaction_tag and entity): error = "All fields must be filled." self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: {error}") self.result_text.configure(state="disabled") return if interaction_tag.startswith("EF") and not (means and means_tag): error = "Means and Means tag are required for Enforcer interaction tags." self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: {error}") self.result_text.configure(state="disabled") return if interaction_tag.startswith("RP") and not (repeater_displayment and repeater_displayment_tag): error = "Displayment and Displayment tag are required for Repeater interaction tags." self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: {error}") self.result_text.configure(state="disabled") return values_text = self.values_text.get("1.0", tk.END).strip() parts_text = self.parts_text.get("1.0", tk.END).strip() limits_text = self.limits_text.get("1.0", tk.END).strip() value_list = [v.strip() for v in values_text.split(",") if v.strip()] value_list = [re.sub(r'^\s*[0-9]+\.\s*', '', v).strip() for v in value_list] parts_list = [p.strip() for p in parts_text.split(",") if p.strip()] parts_list = [re.sub(r'^\s*[0-9]+\.\s*', '', p).strip() for p in parts_list] limits_list = [l.strip() for l in limits_text.split(",") if l.strip()] limits_list = [re.sub(r'^\s*[0-9]+\.\s*', '', l).strip() for l in limits_list] value_tags = [tag.get() for _, tag in self.value_tags if tag.get()] if len(value_list) != len(value_tags): error = f"Mismatch between values ({len(value_list)}) and selected tags ({len(value_tags)}). Please select a tag for each value." self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: {error}") self.result_text.configure(state="disabled") return valid, error = self.validate_chain(interaction_tag, displayment_tag, means_tag, statement_tag, source_tag, value_list, parts_list, limits_list, value_tags, repeater_displayment_tag) if not valid: self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: {error}") self.result_text.configure(state="disabled") return component_positions = self.update_statement_colors() values_str = "Values [" + ", ".join( f"{i+1}. {value} ({value_tags[i]})" for i, value in enumerate(value_list) ) + "]" if interaction_tag.startswith("AC"): interactor_type = "Accepter" elif interaction_tag.startswith("NI"): interactor_type = "Non-Interactor" elif interaction_tag.startswith("EF"): interactor_type = "Enforcer" elif interaction_tag.startswith("RP"): interactor_type = "Repeater" elif interaction_tag.startswith("RE"): interactor_type = "Rejecter" else: interactor_type = "Interactor" keywords = [interactor_type, "Displayment", "Values", "Statement", "Source"] if interactor_type == "Enforcer": chain_prefix = ( f"{interactor_type} [{entity} ({interaction_tag}), Means ({means} ({means_tag}))]-" f"Displayment [{displayment} ({displayment_tag})]-" f"{values_str}-Statement [" ) keywords.append("Means") elif interactor_type == "Repeater": chain_prefix = ( f"{interactor_type} [{entity} ({interaction_tag}), Displayment ({repeater_displayment} ({repeater_displayment_tag}))]-" f"Displayment [{displayment} ({displayment_tag})]-" f"{values_str}-Statement [" ) keywords.append("Displayment") else: chain_prefix = ( f"{interactor_type} [{entity} ({interaction_tag})]-" f"Displayment [{displayment} ({displayment_tag})]-" f"{values_str}-Statement [" ) chain_suffix = f" ({statement_tag})]-Source [{source} ({source_tag})]" self.chain_text = f"{chain_prefix}{statement}{chain_suffix}" self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"{chain_prefix}") statement_start = len(f"{chain_prefix}") self.result_text.insert(tk.END, statement) statement_end = statement_start + len(statement) self.result_text.insert(tk.END, chain_suffix) for start, end, comp, tag in component_positions: self.result_text.tag_add(tag, f"1.0+{statement_start + start}c", f"1.0+{statement_start + end}c") chain_text = f"{chain_prefix}{statement}{chain_suffix}" for keyword in keywords: start = 0 while True: start = chain_text.find(keyword, start) if start == -1: break self.result_text.tag_add("bold", f"1.0+{start}c", f"1.0+{start + len(keyword)}c") start += len(keyword) self.result_text.configure(state="disabled") for widget in self.footnote_container.winfo_children(): widget.destroy() self.transformations = [] self.interaction_tag_value = interaction_tag self.current_tag = interaction_tag if interaction_tag.startswith("RP"): messagebox.showinfo("Info", "No transformations available for Repeaters (RP tags).") else: self.add_footnote_frame(interaction_tag) self.root.update_idletasks() except Exception as e: self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", f"Error: Unexpected issue occurred: {str(e)}") self.result_text.configure(state="disabled") def add_footnote_frame(self, tag): frame = ttk.Frame(self.footnote_container) frame.pack(anchor=tk.W, pady=5, fill=tk.X) label_text = f"Add Transformation for tag {tag}:" ttk.Label(frame, text=label_text).pack(anchor=tk.W) combobox = ttk.Combobox(frame, state="readonly", width=20) combobox.pack(anchor=tk.W, pady=2) valid_transformations = [] if tag in self.forward_conversion_rules: valid_transformations.extend([f"{tag}->{next_tag}" for next_tag in self.forward_conversion_rules[tag]]) if tag in self.forward_transformation_rules: valid_transformations.extend([f"{tag}->{next_tag}" for next_tag in self.forward_transformation_rules[tag]]) combobox['values'] = valid_transformations combobox['state'] = 'readonly' if valid_transformations else 'disabled' add_btn = ttk.Button(frame, text="Add Transformation", command=lambda c=combobox: self.add_transformation(c)) add_btn.pack(anchor=tk.W, pady=5) self.footnote_combobox = combobox self.root.update_idletasks() def add_transformation(self, combobox): try: transformation = combobox.get() if not transformation: messagebox.showerror("Error", "Please select a transformation from the dropdown.") return from_tag, to_tag = transformation.split("->") valid, error = self.validate_transformation(from_tag, to_tag) if not valid: messagebox.showerror("Error", f"Invalid transformation: {error}") return self.transformations.append(transformation) self.current_tag = to_tag valid_transformations = [] if self.current_tag in self.forward_conversion_rules: valid_transformations.extend([f"{self.current_tag}->{next_tag}" for next_tag in self.forward_conversion_rules[self.current_tag]]) if self.current_tag in self.forward_transformation_rules: valid_transformations.extend([f"{self.current_tag}->{next_tag}" for next_tag in self.forward_transformation_rules[self.current_tag]]) combobox['values'] = valid_transformations combobox.set('') combobox['state'] = 'readonly' if valid_transformations else 'disabled' self.update_chain_with_footnote() except Exception as e: messagebox.showerror("Error", f"Failed to add transformation: {str(e)}") def update_chain_with_footnote(self): try: entity = self.entity_text.get().strip() footnote = "" if self.transformations: chain_tags = [self.interaction_tag_value] for trans in self.transformations: _, new_tag = trans.split("->") chain_tags.append(new_tag) # Reverse the chain_tags to display transformations in reverse order chain_tags = chain_tags[::-1] trans_str = "->".join(chain_tags) footnote = f"\nFootnote: '{entity}' underwent {len(self.transformations)} transformation{'s' if len(self.transformations) > 1 else ''}: {trans_str}" self.result_text.configure(state="normal") self.result_text.delete("1.0", tk.END) self.result_text.insert("1.0", self.chain_text + footnote) chain_text_with_footnote = self.chain_text + footnote keywords = ["Accepter", "Non-Interactor", "Enforcer", "Repeater", "Rejecter", "Interactor", "Displayment", "Values", "Statement", "Source", "Means"] for keyword in keywords: start = 0 while True: start = chain_text_with_footnote.find(keyword, start) if start == -1: break self.result_text.tag_add("bold", f"1.0+{start}c", f"1.0+{start + len(keyword)}c") start += len(keyword) self.result_text.configure(state="disabled") except Exception as e: messagebox.showerror("Error", f"Failed to update chain with footnote: {str(e)}") def validate_transformation(self, from_tag, to_tag): if from_tag.startswith("RP"): return False, "Repeaters (RP tags) cannot be transformed." if from_tag.startswith("NI") and not to_tag.startswith("NI"): return False, "Cannot transform NI tags to non-NI types." if from_tag in self.forward_conversion_rules and to_tag in self.forward_conversion_rules[from_tag]: return True, "" if from_tag in self.forward_transformation_rules and to_tag in self.forward_transformation_rules[from_tag]: return True, "" return False, f"{to_tag} cannot be transformed from {from_tag}. Check available transformations for {from_tag}." if __name__ == "__main__": root = tk.Tk() app = ReverseMoralithicChainGeneratorMode4(root) root.mainloop()