# Retry: create the GUI script file attr_form_gui.py in /mnt/data and report result.
import os, textwrap, sys
OUTFILE = "/mnt/data/attr_form_gui.py"
code = r'''"""
attr_form_gui.py
Phase 3: Dynamic GUI form that builds widgets based on attribute_properties JSON and YAML config.
Saves completed work entries into work_properties table as JSON.
Requires: ttkbootstrap, pyyaml
Run: python attr_form_gui.py
"""
import os, sqlite3, json, yaml, datetime, random, string, sys
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import ttkbootstrap as tb
DB_PATH = "wdyd4.db"
YAML_PATH = "attributes_config.yaml"
# ---------- Phase1 utilities (embedded) ----------
def load_config(config_path=YAML_PATH):
if not os.path.exists(config_path):
return {"attributes": [], "attribute_properties": [], "maximum_binded_attribute_support": 1}
with open(config_path, "r", encoding="utf-8") as f:
cfg = yaml.safe_load(f) or {}
cfg.setdefault("attributes", [])
cfg.setdefault("attribute_properties", [])
cfg.setdefault("maximum_binded_attribute_support", 1)
return cfg
def formWorkID(prefix="WDY"):
now = datetime.datetime.utcnow()
core = now.strftime("%y%m%d%H%M%S%f")[:-3]
suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=2))
return f"{prefix}{core}{suffix}"
def _get_db_conn(db_path=DB_PATH):
if not os.path.exists(db_path):
raise FileNotFoundError(f"DB not found at {db_path}")
return sqlite3.connect(db_path)
def get_attribute_properties(attr_name, db_path=DB_PATH):
conn = _get_db_conn(db_path)
cur = conn.cursor()
cur.execute("SELECT properties_json FROM attribute_properties WHERE attribute_name = ?", (attr_name,))
row = cur.fetchone()
conn.close()
if not row: return None
try:
return json.loads(row[0])
except Exception:
return row[0]
def get_dropdown_items(attr_name, db_path=DB_PATH):
props = get_attribute_properties(attr_name, db_path=db_path)
if not props or not isinstance(props, dict): return []
for key in ("DropdownItems","dropdownitems","dropdown_items","dropdown"):
if key in props:
val = props[key]
if isinstance(val, list): return val
try:
parsed = json.loads(val)
if isinstance(parsed, list): return parsed
except Exception:
pass
return []
def get_default_value(attr_name, db_path=DB_PATH):
props = get_attribute_properties(attr_name, db_path=db_path)
if not props or not isinstance(props, dict): return None
for key in ("DefaultValue","Default","defaultvalue","default"):
if key in props: return props[key]
return None
def is_flag_true(attr_name, flag, db_path=DB_PATH):
props = get_attribute_properties(attr_name, db_path=db_path)
if not props or not isinstance(props, dict): return False
if flag in props: return bool(props[flag])
alt = flag.replace(" ", "").replace("_","").lower()
for k,v in props.items():
if k.replace(" ","").replace("_","").lower() == alt:
return bool(v)
return False
# ---------- Phase2 DB helpers (embedded) ----------
def ensure_work_tables(db_path=DB_PATH):
conn = _get_db_conn(db_path); cur = conn.cursor()
cur.execute("""CREATE TABLE IF NOT EXISTS work_properties (
work_id TEXT PRIMARY KEY, user_name TEXT, properties_json TEXT, created_at TEXT)""")
conn.commit(); conn.close()
def list_attributes(db_path=DB_PATH):
conn = _get_db_conn(db_path); cur = conn.cursor()
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Attributes'")
if not cur.fetchone():
conn.close(); return []
cur.execute("SELECT attributeName FROM Attributes ORDER BY attributeID")
rows = [r[0] for r in cur.fetchall()]; conn.close(); return rows
def insert_final_work(work_id, user_name, properties_dict, db_path=DB_PATH):
conn = _get_db_conn(db_path); cur = conn.cursor()
cur.execute("INSERT OR REPLACE INTO work_properties (work_id, user_name, properties_json, created_at) VALUES (?, ?, ?, ?)",
(work_id, user_name, json.dumps(properties_dict, ensure_ascii=False), datetime.datetime.utcnow().isoformat()))
conn.commit(); conn.close()
# ---------- GUI widget constructors ----------
def DrawTheDateEntry(frame, attr_name, default=None):
lbl = ttk.Label(frame, text=attr_name + " (Date):")
ent = ttk.Entry(frame)
if default:
ent.insert(0, default)
lbl.pack(fill="x", padx=4, pady=(4,0))
ent.pack(fill="x", padx=4, pady=(0,6))
return ent
def DrawEntryWidget(frame, attr_name, default=None):
lbl = ttk.Label(frame, text=attr_name + ":")
ent = ttk.Entry(frame)
if default:
ent.insert(0, default)
lbl.pack(fill="x", padx=4, pady=(4,0))
ent.pack(fill="x", padx=4, pady=(0,6))
return ent
def DrawTheCombobox(frame, attr_name, items, default=None):
lbl = ttk.Label(frame, text=attr_name + " (Select):")
combo = ttk.Combobox(frame, values=items)
if default:
combo.set(default)
lbl.pack(fill="x", padx=4, pady=(4,0))
combo.pack(fill="x", padx=4, pady=(0,6))
btn = ttk.Button(frame, text="Add item", command=lambda: handleAddDropdownItem(attr_name, combo))
btn.pack(anchor="e", padx=4, pady=(0,6))
return combo
def handleAddDropdownItem(attr_name, combobox_widget):
new_item = simpledialog.askstring("Add dropdown item", f"New item for {attr_name}:")
if not new_item: return
props = get_attribute_properties(attr_name) or {}
items = props.get("DropdownItems", [])
if not isinstance(items, list): items = []
items.append(new_item)
props["DropdownItems"] = items
conn = _get_db_conn()
cur = conn.cursor()
cur.execute("UPDATE attribute_properties SET properties_json = ? WHERE attribute_name = ?", (json.dumps(props, ensure_ascii=False), attr_name))
conn.commit(); conn.close()
combobox_widget['values'] = items
combobox_widget.set(new_item)
messagebox.showinfo("Added", f"Added '{new_item}' to {attr_name} dropdown items.")
# ---------- Main form builder ----------
class DynamicFormApp:
def __init__(self, master, user_name="default"):
self.master = master
self.user_name = user_name
master.title("WDYD Dynamic Form")
ensure_work_tables()
self.cfg = load_config()
self.universal = self._build_universal_dict()
self.mandatory_attrs = self.universal.get("Mandatory", [])
if not self.mandatory_attrs:
self.mandatory_attrs = list_attributes()
self.widgets = {}
self._build_ui()
def _build_universal_dict(self):
keys = ["Mandatory","HasDropdown","IsDate","HasDefault","HasBinded","CanHaveMultipleValueInLifetime"]
d = {k: [] for k in keys}
conn = _get_db_conn(); cur = conn.cursor()
cur.execute("SELECT attribute_name, properties_json FROM attribute_properties")
for name, pjson in cur.fetchall():
try:
props = json.loads(pjson)
except Exception:
continue
for k in keys:
for pk, pv in props.items():
if pk.replace(" ", "").lower() == k.replace(" ",").lower():
if pv:
d[k].append(name)
break
conn.close()
return d
def _build_ui(self):
style = tb.Style(theme="litera")
frame = ttk.Frame(self.master, padding=12)
frame.pack(fill="both", expand=True)
form_area = ttk.Frame(frame)
form_area.pack(fill="both", expand=True)
for attr in self.mandatory_attrs:
if is_flag_true(attr, "IsDate"):
default = get_default_value(attr)
wid = DrawTheDateEntry(form_area, attr, default=default)
elif is_flag_true(attr, "HasDropdown"):
items = get_dropdown_items(attr)
default = get_default_value(attr)
wid = DrawTheCombobox(form_area, attr, items, default=default)
else:
default = get_default_value(attr)
wid = DrawEntryWidget(form_area, attr, default=default)
self.widgets[attr] = wid
btn = ttk.Button(frame, text="Submit", bootstyle="success", command=self.submit)
btn.pack(pady=8)
def submit(self):
data = {}
for attr, widget in self.widgets.items():
try:
val = widget.get()
except Exception:
val = ""
data[attr] = val
wid = formWorkID()
insert_final_work(wid, self.user_name, data)
messagebox.showinfo("Saved", f"Work saved with ID: {wid}")
def main():
root = tb.Window(themename="litera")
app = DynamicFormApp(root)
root.mainloop()
if __name__ == "__main__":
main()
'''
try:
with open(OUTFILE, "w", encoding="utf-8") as f:
f.write(code)
created = True
except Exception as e:
created = False
err = str(e)
{"created_file": OUTFILE, "created": created, "error": err if not created else None}