From a0fcfc93b6c89e92f3e086f9b9c36ade32c95dca Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 16 Oct 2026 15:20:14 +0530 Subject: [PATCH] Title-case subtopic names via scripts/titlecase.py 300 of 483 subtopics normalized ('Addition And Subtraction Formula' -> 'Addition and Subtraction Formula'). Acronym FIX map preserves AC/AM/GM/HM/AP/GP/EM/EMF/RMS/SHM/GOC/IUPAC/pH; article-A and PN Junction fixed; VSEPR and Raoult's Law proper nouns corrected. Chapter names were already title case. Applied to live tablet DB. --- scripts/__pycache__/titlecase.cpython-314.pyc | Bin 0 -> 3910 bytes scripts/titlecase.py | 60 ++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 scripts/__pycache__/titlecase.cpython-314.pyc create mode 100644 scripts/titlecase.py diff --git a/scripts/__pycache__/titlecase.cpython-314.pyc b/scripts/__pycache__/titlecase.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4304c00eb2efb9c411f32263823c10160473779f GIT binary patch literal 3910 zcmbtWTTENY8J=_T1>2ZAhJ+;sLa2en+!Dw_l68!^L!4tUtJJ}A>;ss!eVpTCSWwft z(n^I*Hc%;2-73;j?XF6-sl+3FsLE5HS&=5*X{3$RzT}NGT2oHGo>c3BXA} z9pD{6J>V3;4rl;00-6A)0nLCjA5ap@wQw}|BhJb>xDu|FE9Kg_GOnE~=gx8!+&PZr zoLmjp!P&S@PS1652B$^Raosk=_1I9XRMKISB+V~WF+8&lUXHG=6-7!b$w0$>pkN&*KgCNaau z=P@J3Q{3KKQD;!kdz{0HR7wzMIp{R1(<`dDmmk; z_LP*KpSQ2_A!kw)miVxga;_|fVti8Mlg?CFj3%K*ib^p)97^%$lB?Jf42BX3K?+HH zFevteU$VI=vX>e3FH}Xt$$C_t zq|Tuv9U%SXV2!hxXV6`4zy~YZ*0da7${A`cvC#m!6TGZx+N)l{8 z?IrViBf%Hy+)knKiwOC30a+19Hz=Y7a01`zyv1*P2t{oL+BlKZq;H@^>*?2NzUSD! zcVW~2+j-m}^YE5wehO9_F{$_K1NnPL<{5GR&>Fo}1s@uNmF7M8^{?Gma_Aswvxmt1 z1!klVR4esLWFlAP5t%vEw*uqCVSzX?$5x-;M^J-&Op(%Cj@0+<$lGQ+>+kuGS$U{& zSi%3~Wo8Z?U#YiNTT#JkG4|^?`nA44upjM(&(T}wIZrM{J>nSe$zxqr>Zi3x!( z-kJOHoa^k3(Al+)vlnl+{p7XMX^h@|K~C%x2$tPp#ky277L{-jpGe1f5iTyKMIIC) zeGBVD$t0hMV0PT&@nSp2nHZnIx|py+_|WM4*Z_JE3x#>C^G&)YCa_NF$1Vxc zgu@_SfYGpSMGzyPbwXOgR1{Mym<N6 z40>)U-5lE(yE~EbBO%mKArl_)T7jH&zGxvu17z8qocKab7^DgZfu>& znXM0+?!B9D|JCX*SGOxaPwjMn@!_GZ42>f2Ou>6*K46(351l_LOf4`Z;0FqCPUJq*b5~GE%uR0 zCYq2Olt`pPc`xLRk|kcwT1kVZ^H!?jC4%fh(-AQbo8(eqy3kin@W-U+{C#_mVW+YI3UHNL=2-+q69YHE2?j^)sbtXi|MW`~kD( z%CFDQ!Z5o`v}*z0j5;(9|9jsvDm9z#rnP&03i(-3v}5l9jK$8C5QSGgT|y|3Z};mh z@Mq{ZSWp+EjSPx|PxP95{ZF@6?2v4Ok}`Sbq3_px`(&w`HV*6Mwp z36Fc)9*wly$q$5mh`Tmv2VszeWHg*wJ2T@QbWM8-HFlq8TJCx2ihXv(!+GG-OIOzP zcKg}OcKce9DZCg;EbtNg8fzEk?T#XBNZkhU;k&UREF==}hk^Csw3re^Y~VlS!)Z_> z<2*0Hcd?i(1X!08;Uq#gAs&xP;s~tSVH9V8xKVj;sVsm$9NY=20b&CZ8|@8W?K$TjTv{&RJ_@`(R#NXsM)&NztMkS z>pe90JvF<&GP@4V?#y7$T9O(5TUG5w(K_?YT9d6jwARboalfA^$N0whLCt&n-TSSN zEt5G*#n$alCf0{@CDqx+`*)s|R%I)*`t1C+Z`*TF-TI`o?PW2lK5=A4I^!?Kem?fm z#51;NeQaxGE1H!)cki&Djc)fJn4FKw%fCnRqLeK%4#b5>I>uiXH=sXxz*75=A2Ad~eS>U=$o5Y?UF6K@j!d+1 zlv-zxkZh+FyVb1NtXiL)QOeuBirqP{*xf4CN{ZbZRP27GY9orx87)T)86a0&rjqh% zmDohA9U1GnwL&G8wJJH$ppwQj zDrsp`NxM@e9o;JF=~YQzze)zKsN_AjN(P4&l3|Zb$Y@!g-s*vaDQ!>h^z2sdN_*2e zgITRiZ}U4dyPm!C`^`CnWu4!e%`U*lhL#NTtr33jzwH3+hc}D0tgk(0YQJWU>lK?d V8#P-EpPYQcR=s2pdqUpfe*;+1Q1Ac% literal 0 HcmV?d00001 diff --git a/scripts/titlecase.py b/scripts/titlecase.py new file mode 100644 index 0000000..9de0034 --- /dev/null +++ b/scripts/titlecase.py @@ -0,0 +1,60 @@ +import sqlite3, sys, re + +# Small words stay lowercase in title case (unless first/last word) +SMALL = {"a","an","the","and","or","nor","but","for","yet","so","as","at", + "by","for","from","in","into","of","on","onto","per","to","with", + "vs","via","de","van","der","and/or"} + +# Acronyms and special tokens that keep their form +KEEP = {"AC","DC","EMF","pH","GOC","IUPAC","SN1","SN2","E1","E2","3D","2D", + "AM","GM","HM","AP","GP","HP","LCM","HCF","EM","SHM","RMS","SI", + "MCQ","JEE","NTA","AIEEE","S1","S2","N","Z","Q","R","C","O","H"} + +# Common mis-cased words found in the bank -> correct form +FIX = {"ac": "AC", "am": "AM", "gm": "GM", "hm": "HM", "ap": "AP", + "gp": "GP", "hp": "HP", "emf": "EMF", "rms": "RMS", "shm": "SHM", + "goc": "GOC", "iupac": "IUPAC", "em": "EM"} + +def title_case(name: str) -> str: + # Preserve existing mixed-case names that are already fine + words = name.split() + out = [] + for i, w in enumerate(words): + # Keep tokens with internal capitals/digits (e.g. pH, 3D, S1) + core = re.sub(r'^[^A-Za-z0-9]+|[^A-Za-z0-9]+$', '', w) + if not core: + out.append(w) + continue + if core in KEEP or (core.isupper() and len(core) <= 4): + out.append(w) + continue + low = core.lower() + if low in FIX: + out.append(w.replace(core, FIX[low])) + elif low in SMALL and i != 0 and i != len(words) - 1: + # lowercase the core, keep surrounding punctuation + out.append(w.replace(core, low)) + else: + out.append(w.replace(core, core[0].upper() + core[1:].lower())) + return " ".join(out) + + +def main(db_path: str) -> int: + con = sqlite3.connect(db_path) + cur = con.cursor() + rows = cur.execute("SELECT id, name FROM subtopics").fetchall() + changed = 0 + for sid, name in rows: + new = title_case(name) + if new != name: + cur.execute("UPDATE subtopics SET name=? WHERE id=?", (new, sid)) + changed += 1 + if changed <= 15: + print(f"{name} -> {new}") + con.commit() + print(f"\nchanged {changed} of {len(rows)}") + return changed + + +if __name__ == "__main__": + main(sys.argv[1])