_sync_all.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. #!/usr/bin/env python3
  2. """
  3. Sync colors.csv and ui-reasoning.csv with the updated products.csv (161 entries).
  4. - Remove deleted product types
  5. - Rename mismatched entries
  6. - Add new entries for missing product types
  7. - Keep colors.csv aligned 1:1 with products.csv
  8. - Renumber everything
  9. """
  10. import csv, os, json
  11. BASE = os.path.dirname(os.path.abspath(__file__))
  12. # ─── Color derivation helpers ────────────────────────────────────────────────
  13. def h2r(h):
  14. h = h.lstrip("#")
  15. return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
  16. def r2h(r, g, b):
  17. return f"#{max(0,min(255,int(r))):02X}{max(0,min(255,int(g))):02X}{max(0,min(255,int(b))):02X}"
  18. def lum(h):
  19. r, g, b = [x/255.0 for x in h2r(h)]
  20. r, g, b = [(x/12.92 if x<=0.03928 else ((x+0.055)/1.055)**2.4) for x in (r, g, b)]
  21. return 0.2126*r + 0.7152*g + 0.0722*b
  22. def is_dark(bg):
  23. return lum(bg) < 0.18
  24. def on_color(bg):
  25. return "#FFFFFF" if lum(bg) < 0.4 else "#0F172A"
  26. def blend(a, b, f=0.15):
  27. ra, ga, ba = h2r(a)
  28. rb, gb, bb = h2r(b)
  29. return r2h(ra+(rb-ra)*f, ga+(gb-ga)*f, ba+(bb-ba)*f)
  30. def shift(h, n):
  31. r, g, b = h2r(h)
  32. return r2h(r+n, g+n, b+n)
  33. def derive_row(pt, pri, sec, acc, bg, notes=""):
  34. """Generate full 16-token color row from 4 base colors."""
  35. dark = is_dark(bg)
  36. fg = "#FFFFFF" if dark else "#0F172A"
  37. on_pri = on_color(pri)
  38. on_sec = on_color(sec)
  39. on_acc = on_color(acc)
  40. card = shift(bg, 10) if dark else "#FFFFFF"
  41. card_fg = "#FFFFFF" if dark else "#0F172A"
  42. muted = blend(bg, pri, 0.08) if dark else blend("#FFFFFF", pri, 0.06)
  43. muted_fg = "#94A3B8" if dark else "#64748B"
  44. border = f"rgba(255,255,255,0.08)" if dark else blend("#FFFFFF", pri, 0.12)
  45. destr = "#DC2626"
  46. on_destr = "#FFFFFF"
  47. ring = pri
  48. return [pt, pri, on_pri, sec, on_sec, acc, on_acc, bg, fg, card, card_fg, muted, muted_fg, border, destr, on_destr, ring, notes]
  49. # ─── Rename maps ─────────────────────────────────────────────────────────────
  50. COLOR_RENAMES = {
  51. "Quantum Computing": "Quantum Computing Interface",
  52. "Biohacking / Longevity": "Biohacking / Longevity App",
  53. "Autonomous Systems": "Autonomous Drone Fleet Manager",
  54. "Generative AI Art": "Generative Art Platform",
  55. "Spatial / Vision OS": "Spatial Computing OS / App",
  56. "Climate Tech": "Sustainable Energy / Climate Tech",
  57. }
  58. UI_RENAMES = {
  59. "Architecture/Interior": "Architecture / Interior",
  60. "Autonomous Drone Fleet": "Autonomous Drone Fleet Manager",
  61. "B2B SaaS Enterprise": "B2B Service",
  62. "Biohacking/Longevity App": "Biohacking / Longevity App",
  63. "Biotech/Life Sciences": "Biotech / Life Sciences",
  64. "Developer Tool/IDE": "Developer Tool / IDE",
  65. "Education": "Educational App",
  66. "Fintech (Banking)": "Fintech/Crypto",
  67. "Government/Public": "Government/Public Service",
  68. "Home Services": "Home Services (Plumber/Electrician)",
  69. "Micro-Credentials/Badges": "Micro-Credentials/Badges Platform",
  70. "Music/Entertainment": "Music Streaming",
  71. "Quantum Computing": "Quantum Computing Interface",
  72. "Real Estate": "Real Estate/Property",
  73. "Remote Work/Collaboration": "Remote Work/Collaboration Tool",
  74. "Restaurant/Food": "Restaurant/Food Service",
  75. "SaaS Dashboard": "Analytics Dashboard",
  76. "Space Tech/Aerospace": "Space Tech / Aerospace",
  77. "Spatial Computing OS": "Spatial Computing OS / App",
  78. "Startup Landing": "Micro SaaS",
  79. "Sustainable Energy/Climate": "Sustainable Energy / Climate Tech",
  80. "Travel/Tourism": "Travel/Tourism Agency",
  81. "Wellness/Mental Health": "Mental Health App",
  82. }
  83. REMOVE_TYPES = {
  84. "Service Landing Page", "Sustainability/ESG Platform",
  85. "Cleaning Service", "Coffee Shop",
  86. "Consulting Firm", "Conference/Webinar Platform",
  87. }
  88. # ─── New color definitions: (primary, secondary, accent, bg, notes) ──────────
  89. # Grouped by category for clarity. Each tuple generates a full 16-token row.
  90. NEW_COLORS = {
  91. # ── Old #97-#116 that never got colors ──
  92. "Todo & Task Manager": ("#2563EB","#3B82F6","#059669","#F8FAFC","Functional blue + progress green"),
  93. "Personal Finance Tracker": ("#1E40AF","#3B82F6","#059669","#0F172A","Trust blue + profit green on dark"),
  94. "Chat & Messaging App": ("#2563EB","#6366F1","#059669","#FFFFFF","Messenger blue + online green"),
  95. "Notes & Writing App": ("#78716C","#A8A29E","#D97706","#FFFBEB","Warm ink + amber accent on cream"),
  96. "Habit Tracker": ("#D97706","#F59E0B","#059669","#FFFBEB","Streak amber + habit green"),
  97. "Food Delivery / On-Demand": ("#EA580C","#F97316","#2563EB","#FFF7ED","Appetizing orange + trust blue"),
  98. "Ride Hailing / Transportation":("#1E293B","#334155","#2563EB","#0F172A","Map dark + route blue"),
  99. "Recipe & Cooking App": ("#9A3412","#C2410C","#059669","#FFFBEB","Warm terracotta + fresh green"),
  100. "Meditation & Mindfulness": ("#7C3AED","#8B5CF6","#059669","#FAF5FF","Calm lavender + mindful green"),
  101. "Weather App": ("#0284C7","#0EA5E9","#F59E0B","#F0F9FF","Sky blue + sun amber"),
  102. "Diary & Journal App": ("#92400E","#A16207","#6366F1","#FFFBEB","Warm journal brown + ink violet"),
  103. "CRM & Client Management": ("#2563EB","#3B82F6","#059669","#F8FAFC","Professional blue + deal green"),
  104. "Inventory & Stock Management":("#334155","#475569","#059669","#F8FAFC","Industrial slate + stock green"),
  105. "Flashcard & Study Tool": ("#7C3AED","#8B5CF6","#059669","#FAF5FF","Study purple + correct green"),
  106. "Booking & Appointment App": ("#0284C7","#0EA5E9","#059669","#F0F9FF","Calendar blue + available green"),
  107. "Invoice & Billing Tool": ("#1E3A5F","#2563EB","#059669","#F8FAFC","Navy professional + paid green"),
  108. "Grocery & Shopping List": ("#059669","#10B981","#D97706","#ECFDF5","Fresh green + food amber"),
  109. "Timer & Pomodoro": ("#DC2626","#EF4444","#059669","#0F172A","Focus red on dark + break green"),
  110. "Parenting & Baby Tracker": ("#EC4899","#F472B6","#0284C7","#FDF2F8","Soft pink + trust blue"),
  111. "Scanner & Document Manager": ("#1E293B","#334155","#2563EB","#F8FAFC","Document grey + scan blue"),
  112. # ── A. Utility / Productivity ──
  113. "Calendar & Scheduling App": ("#2563EB","#3B82F6","#059669","#F8FAFC","Calendar blue + event green"),
  114. "Password Manager": ("#1E3A5F","#334155","#059669","#0F172A","Vault dark blue + secure green"),
  115. "Expense Splitter / Bill Split":("#059669","#10B981","#DC2626","#F8FAFC","Balance green + owe red"),
  116. "Voice Recorder & Memo": ("#DC2626","#EF4444","#2563EB","#FFFFFF","Recording red + waveform blue"),
  117. "Bookmark & Read-Later": ("#D97706","#F59E0B","#2563EB","#FFFBEB","Warm amber + link blue"),
  118. "Translator App": ("#2563EB","#0891B2","#EA580C","#F8FAFC","Global blue + teal + accent orange"),
  119. "Calculator & Unit Converter": ("#EA580C","#F97316","#2563EB","#1C1917","Operation orange on dark"),
  120. "Alarm & World Clock": ("#D97706","#F59E0B","#6366F1","#0F172A","Time amber + night indigo on dark"),
  121. "File Manager & Transfer": ("#2563EB","#3B82F6","#D97706","#F8FAFC","Folder blue + file amber"),
  122. "Email Client": ("#2563EB","#3B82F6","#DC2626","#FFFFFF","Inbox blue + priority red"),
  123. # ── B. Games ──
  124. "Casual Puzzle Game": ("#EC4899","#8B5CF6","#F59E0B","#FDF2F8","Cheerful pink + reward gold"),
  125. "Trivia & Quiz Game": ("#2563EB","#7C3AED","#F59E0B","#EFF6FF","Quiz blue + gold leaderboard"),
  126. "Card & Board Game": ("#15803D","#166534","#D97706","#0F172A","Felt green + gold on dark"),
  127. "Idle & Clicker Game": ("#D97706","#F59E0B","#7C3AED","#FFFBEB","Coin gold + prestige purple"),
  128. "Word & Crossword Game": ("#15803D","#059669","#D97706","#FFFFFF","Word green + letter amber"),
  129. "Arcade & Retro Game": ("#DC2626","#2563EB","#22C55E","#0F172A","Neon red+blue on dark + score green"),
  130. # ── C. Creator Tools ──
  131. "Photo Editor & Filters": ("#7C3AED","#6366F1","#0891B2","#0F172A","Editor violet + filter cyan on dark"),
  132. "Short Video Editor": ("#EC4899","#DB2777","#2563EB","#0F172A","Video pink on dark + timeline blue"),
  133. "Drawing & Sketching Canvas": ("#7C3AED","#8B5CF6","#0891B2","#1C1917","Canvas purple + tool teal on dark"),
  134. "Music Creation & Beat Maker": ("#7C3AED","#6366F1","#22C55E","#0F172A","Studio purple + waveform green on dark"),
  135. "Meme & Sticker Maker": ("#EC4899","#F59E0B","#2563EB","#FFFFFF","Viral pink + comedy yellow + share blue"),
  136. "AI Photo & Avatar Generator": ("#7C3AED","#6366F1","#EC4899","#FAF5FF","AI purple + generation pink"),
  137. "Link-in-Bio Page Builder": ("#2563EB","#7C3AED","#EC4899","#FFFFFF","Brand blue + creator purple"),
  138. # ── D. Personal Life ──
  139. "Wardrobe & Outfit Planner": ("#BE185D","#EC4899","#D97706","#FDF2F8","Fashion rose + gold accent"),
  140. "Plant Care Tracker": ("#15803D","#059669","#D97706","#F0FDF4","Nature green + sun yellow"),
  141. "Book & Reading Tracker": ("#78716C","#92400E","#D97706","#FFFBEB","Book brown + page amber"),
  142. "Couple & Relationship App": ("#BE185D","#EC4899","#DC2626","#FDF2F8","Romance rose + love red"),
  143. "Family Calendar & Chores": ("#2563EB","#059669","#D97706","#F8FAFC","Family blue + chore green"),
  144. "Mood Tracker": ("#7C3AED","#6366F1","#D97706","#FAF5FF","Mood purple + insight amber"),
  145. "Gift & Wishlist": ("#DC2626","#D97706","#EC4899","#FFF1F2","Gift red + gold + surprise pink"),
  146. # ── E. Health ──
  147. "Running & Cycling GPS": ("#EA580C","#F97316","#059669","#0F172A","Energetic orange + pace green on dark"),
  148. "Yoga & Stretching Guide": ("#6B7280","#78716C","#0891B2","#F5F5F0","Sage neutral + calm teal"),
  149. "Sleep Tracker": ("#4338CA","#6366F1","#7C3AED","#0F172A","Night indigo + dream violet on dark"),
  150. "Calorie & Nutrition Counter": ("#059669","#10B981","#EA580C","#ECFDF5","Healthy green + macro orange"),
  151. "Period & Cycle Tracker": ("#BE185D","#EC4899","#7C3AED","#FDF2F8","Blush rose + fertility lavender"),
  152. "Medication & Pill Reminder": ("#0284C7","#0891B2","#DC2626","#F0F9FF","Medical blue + alert red"),
  153. "Water & Hydration Reminder": ("#0284C7","#06B6D4","#0891B2","#F0F9FF","Refreshing blue + water cyan"),
  154. "Fasting & Intermittent Timer":("#6366F1","#4338CA","#059669","#0F172A","Fasting indigo on dark + eating green"),
  155. # ── F. Social ──
  156. "Anonymous Community / Confession":("#475569","#334155","#0891B2","#0F172A","Protective grey + subtle teal on dark"),
  157. "Local Events & Discovery": ("#EA580C","#F97316","#2563EB","#FFF7ED","Event orange + map blue"),
  158. "Study Together / Virtual Coworking":("#2563EB","#3B82F6","#059669","#F8FAFC","Focus blue + session green"),
  159. # ── G. Education ──
  160. "Coding Challenge & Practice": ("#22C55E","#059669","#D97706","#0F172A","Code green + difficulty amber on dark"),
  161. "Kids Learning (ABC & Math)": ("#2563EB","#F59E0B","#EC4899","#EFF6FF","Learning blue + play yellow + fun pink"),
  162. "Music Instrument Learning": ("#DC2626","#9A3412","#D97706","#FFFBEB","Musical red + warm amber"),
  163. # ── H. Transport ──
  164. "Parking Finder": ("#2563EB","#059669","#DC2626","#F0F9FF","Available blue/green + occupied red"),
  165. "Public Transit Guide": ("#2563EB","#0891B2","#EA580C","#F8FAFC","Transit blue + line colors"),
  166. "Road Trip Planner": ("#EA580C","#0891B2","#D97706","#FFF7ED","Adventure orange + map teal"),
  167. # ── I. Safety & Lifestyle ──
  168. "VPN & Privacy Tool": ("#1E3A5F","#334155","#22C55E","#0F172A","Shield dark + connected green"),
  169. "Emergency SOS & Safety": ("#DC2626","#EF4444","#2563EB","#FFF1F2","Alert red + safety blue"),
  170. "Wallpaper & Theme App": ("#7C3AED","#EC4899","#2563EB","#FAF5FF","Aesthetic purple + trending pink"),
  171. "White Noise & Ambient Sound": ("#475569","#334155","#4338CA","#0F172A","Ambient grey + deep indigo on dark"),
  172. "Home Decoration & Interior Design":("#78716C","#A8A29E","#D97706","#FAF5F2","Interior warm grey + gold accent"),
  173. }
  174. # ─── 1. REBUILD colors.csv ───────────────────────────────────────────────────
  175. def rebuild_colors():
  176. src = os.path.join(BASE, "colors.csv")
  177. with open(src, newline="", encoding="utf-8") as f:
  178. reader = csv.DictReader(f)
  179. headers = reader.fieldnames
  180. existing = list(reader)
  181. # Build lookup: Product Type -> row data
  182. color_map = {}
  183. for row in existing:
  184. pt = row.get("Product Type", "").strip()
  185. if not pt:
  186. continue
  187. # Remove deleted types
  188. if pt in REMOVE_TYPES:
  189. print(f" [colors] REMOVE: {pt}")
  190. continue
  191. # Rename mismatched types
  192. if pt in COLOR_RENAMES:
  193. new_name = COLOR_RENAMES[pt]
  194. print(f" [colors] RENAME: {pt} → {new_name}")
  195. row["Product Type"] = new_name
  196. pt = new_name
  197. color_map[pt] = row
  198. # Read products.csv to get the correct order
  199. with open(os.path.join(BASE, "products.csv"), newline="", encoding="utf-8") as f:
  200. products = list(csv.DictReader(f))
  201. # Build final rows in products.csv order
  202. final_rows = []
  203. added = 0
  204. for i, prod in enumerate(products, 1):
  205. pt = prod["Product Type"]
  206. if pt in color_map:
  207. row = color_map[pt]
  208. row["No"] = str(i)
  209. final_rows.append(row)
  210. elif pt in NEW_COLORS:
  211. pri, sec, acc, bg, notes = NEW_COLORS[pt]
  212. new_row = derive_row(pt, pri, sec, acc, bg, notes)
  213. d = dict(zip(headers, [str(i)] + new_row))
  214. final_rows.append(d)
  215. added += 1
  216. else:
  217. print(f" [colors] WARNING: No color data for '{pt}' - using defaults")
  218. new_row = derive_row(pt, "#2563EB", "#3B82F6", "#059669", "#F8FAFC", "Auto-generated default")
  219. d = dict(zip(headers, [str(i)] + new_row))
  220. final_rows.append(d)
  221. added += 1
  222. # Write
  223. with open(src, "w", newline="", encoding="utf-8") as f:
  224. writer = csv.DictWriter(f, fieldnames=headers)
  225. writer.writeheader()
  226. writer.writerows(final_rows)
  227. product_count = len(products)
  228. print(f"\n ✅ colors.csv: {len(final_rows)} rows ({product_count} products)")
  229. print(f" Added: {added} new color rows")
  230. # ─── 2. REBUILD ui-reasoning.csv ─────────────────────────────────────────────
  231. def derive_ui_reasoning(prod):
  232. """Generate ui-reasoning row from products.csv row."""
  233. pt = prod["Product Type"]
  234. style = prod.get("Primary Style Recommendation", "")
  235. landing = prod.get("Landing Page Pattern", "")
  236. color_focus = prod.get("Color Palette Focus", "")
  237. considerations = prod.get("Key Considerations", "")
  238. keywords = prod.get("Keywords", "")
  239. # Typography mood derived from style
  240. typo_map = {
  241. "Minimalism": "Professional + Clean hierarchy",
  242. "Glassmorphism": "Modern + Clear hierarchy",
  243. "Brutalism": "Bold + Oversized + Monospace",
  244. "Claymorphism": "Playful + Rounded + Friendly",
  245. "Dark Mode": "High contrast + Light on dark",
  246. "Neumorphism": "Subtle + Soft + Monochromatic",
  247. "Flat Design": "Bold + Clean + Sans-serif",
  248. "Vibrant": "Energetic + Bold + Large",
  249. "Aurora": "Elegant + Gradient-friendly",
  250. "AI-Native": "Conversational + Minimal chrome",
  251. "Organic": "Warm + Humanist + Natural",
  252. "Motion": "Dynamic + Hierarchy-shifting",
  253. "Accessible": "Large + High contrast + Clear",
  254. "Soft UI": "Modern + Accessible + Balanced",
  255. "Trust": "Professional + Serif accents",
  256. "Swiss": "Grid-based + Mathematical + Helvetica",
  257. "3D": "Immersive + Spatial + Variable",
  258. "Retro": "Nostalgic + Monospace + Neon",
  259. "Cyberpunk": "Terminal + Monospace + Neon",
  260. "Pixel": "Retro + Blocky + 8-bit",
  261. }
  262. typo_mood = "Professional + Clear hierarchy"
  263. for key, val in typo_map.items():
  264. if key.lower() in style.lower():
  265. typo_mood = val
  266. break
  267. # Key effects from style
  268. eff_map = {
  269. "Glassmorphism": "Backdrop blur (10-20px) + Translucent overlays",
  270. "Neumorphism": "Dual shadows (light+dark) + Soft press 150ms",
  271. "Claymorphism": "Multi-layer shadows + Spring bounce + Soft press 200ms",
  272. "Brutalism": "No transitions + Hard borders + Instant feedback",
  273. "Dark Mode": "Subtle glow + Neon accents + High contrast",
  274. "Flat Design": "Color shift hover + Fast 150ms transitions + No shadows",
  275. "Minimalism": "Subtle hover 200ms + Smooth transitions + Clean",
  276. "Motion-Driven": "Scroll animations + Parallax + Page transitions",
  277. "Micro-interactions": "Haptic feedback + Small 50-100ms animations",
  278. "Vibrant": "Large section gaps 48px+ + Color shift hover + Scroll-snap",
  279. "Aurora": "Flowing gradients 8-12s + Color morphing",
  280. "AI-Native": "Typing indicator + Streaming text + Context reveal",
  281. "Organic": "Rounded 16-24px + Natural shadows + Flowing SVG",
  282. "Soft UI": "Improved shadows + Modern 200-300ms + Focus visible",
  283. "3D": "WebGL/Three.js + Parallax 3-5 layers + Physics 300-400ms",
  284. "Trust": "Clear focus rings + Badge hover + Metric pulse",
  285. "Accessible": "Focus rings 3-4px + ARIA + Reduced motion",
  286. }
  287. key_effects = "Subtle hover (200ms) + Smooth transitions"
  288. for key, val in eff_map.items():
  289. if key.lower() in style.lower():
  290. key_effects = val
  291. break
  292. # Decision rules
  293. rules = {}
  294. if "dark" in style.lower() or "oled" in style.lower():
  295. rules["if_light_mode_needed"] = "provide-theme-toggle"
  296. if "glass" in style.lower():
  297. rules["if_low_performance"] = "fallback-to-flat"
  298. if "conversion" in landing.lower():
  299. rules["if_conversion_focused"] = "add-urgency-colors"
  300. if "social" in landing.lower():
  301. rules["if_trust_needed"] = "add-testimonials"
  302. if "data" in keywords.lower() or "dashboard" in keywords.lower():
  303. rules["if_data_heavy"] = "prioritize-data-density"
  304. if not rules:
  305. rules["if_ux_focused"] = "prioritize-clarity"
  306. rules["if_mobile"] = "optimize-touch-targets"
  307. # Anti-patterns
  308. anti_patterns = []
  309. if "minimalism" in style.lower() or "minimal" in style.lower():
  310. anti_patterns.append("Excessive decoration")
  311. if "dark" in style.lower():
  312. anti_patterns.append("Pure white backgrounds")
  313. if "flat" in style.lower():
  314. anti_patterns.append("Complex shadows + 3D effects")
  315. if "vibrant" in style.lower():
  316. anti_patterns.append("Muted colors + Low energy")
  317. if "accessible" in style.lower():
  318. anti_patterns.append("Color-only indicators")
  319. if not anti_patterns:
  320. anti_patterns = ["Inconsistent styling", "Poor contrast ratios"]
  321. anti_str = " + ".join(anti_patterns[:2])
  322. return {
  323. "UI_Category": pt,
  324. "Recommended_Pattern": landing,
  325. "Style_Priority": style,
  326. "Color_Mood": color_focus,
  327. "Typography_Mood": typo_mood,
  328. "Key_Effects": key_effects,
  329. "Decision_Rules": json.dumps(rules),
  330. "Anti_Patterns": anti_str,
  331. "Severity": "HIGH"
  332. }
  333. def rebuild_ui_reasoning():
  334. src = os.path.join(BASE, "ui-reasoning.csv")
  335. with open(src, newline="", encoding="utf-8") as f:
  336. reader = csv.DictReader(f)
  337. headers = reader.fieldnames
  338. existing = list(reader)
  339. # Build lookup
  340. ui_map = {}
  341. for row in existing:
  342. cat = row.get("UI_Category", "").strip()
  343. if not cat:
  344. continue
  345. if cat in REMOVE_TYPES:
  346. print(f" [ui-reason] REMOVE: {cat}")
  347. continue
  348. if cat in UI_RENAMES:
  349. new_name = UI_RENAMES[cat]
  350. print(f" [ui-reason] RENAME: {cat} → {new_name}")
  351. row["UI_Category"] = new_name
  352. cat = new_name
  353. ui_map[cat] = row
  354. with open(os.path.join(BASE, "products.csv"), newline="", encoding="utf-8") as f:
  355. products = list(csv.DictReader(f))
  356. final_rows = []
  357. added = 0
  358. for i, prod in enumerate(products, 1):
  359. pt = prod["Product Type"]
  360. if pt in ui_map:
  361. row = ui_map[pt]
  362. row["No"] = str(i)
  363. final_rows.append(row)
  364. else:
  365. row = derive_ui_reasoning(prod)
  366. row["No"] = str(i)
  367. final_rows.append(row)
  368. added += 1
  369. with open(src, "w", newline="", encoding="utf-8") as f:
  370. writer = csv.DictWriter(f, fieldnames=headers)
  371. writer.writeheader()
  372. writer.writerows(final_rows)
  373. print(f"\n ✅ ui-reasoning.csv: {len(final_rows)} rows")
  374. print(f" Added: {added} new reasoning rows")
  375. # ─── MAIN ────────────────────────────────────────────────────────────────────
  376. if __name__ == "__main__":
  377. print("=== Rebuilding colors.csv ===")
  378. rebuild_colors()
  379. print("\n=== Rebuilding ui-reasoning.csv ===")
  380. rebuild_ui_reasoning()
  381. print("\n🎉 Done!")