From 4b6a621cc01c6e1d077a5e2f826755851846836e Mon Sep 17 00:00:00 2001 From: Watthana Kayowaen Date: Tue, 3 Feb 2026 13:51:57 +0700 Subject: [PATCH 1/3] chore: update compiled Python bytecode files in __pycache__ directories --- __pycache__/config.cpython-313.pyc | Bin 1233 -> 1246 bytes __pycache__/database.cpython-313.pyc | Bin 15726 -> 15726 bytes .../__pycache__/ml_mapper.cpython-313.pyc | Bin 17557 -> 18223 bytes .../__pycache__/transformers.cpython-313.pyc | Bin 11107 -> 12768 bytes .../migration_engine.cpython-313.pyc | Bin 26359 -> 43589 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc index da2285701adf57f412aedd0a7e1622d22c2b1923..28c900bfc46ac2a638b3bf233c22a96db19be8a7 100644 GIT binary patch delta 79 zcmcb}d5@F#GcPX}0}$Mq)tLEuBku%8Mv=+$829pUySw_i204bf#(Vf}j$(Ss$Y?s* gfu&rETb$_vgYYLQ7EvHA_=$&s$GVZD$QWn>0LAV diff --git a/__pycache__/database.cpython-313.pyc b/__pycache__/database.cpython-313.pyc index e358c7169684c57386d330b7c61ad3d132953757..81768095383880ae2dea4635fdd02b1716c2cc60 100644 GIT binary patch delta 20 acmaD?^{$HhGcPX}0}$Mq)wq#6*%km*VFyM4 delta 20 acmaD?^{$HhGcPX}0}u%8wcp5{YzqKSRR&uC diff --git a/services/__pycache__/ml_mapper.cpython-313.pyc b/services/__pycache__/ml_mapper.cpython-313.pyc index c2295fb47db9d6531e6ef6a5c026ee44dc6197de..dfec9a0d88549e00c78ae59525a2f148f560b2e9 100644 GIT binary patch delta 1056 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 p>^lJb(m;3X629$R)0U6s=2HtJ39gf*!a_9gW;M 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 m-bLX@)dorWOzn+b*Pwie&Ux^ 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 From b9fabff780f14ca09ce4cefc8032f913fd9ad423 Mon Sep 17 00:00:00 2001 From: Watthana Kayowaen Date: Wed, 4 Feb 2026 10:06:14 +0700 Subject: [PATCH 2/3] chore: update compiled Python bytecode files and add migration checkpoint JSON --- __pycache__/config.cpython-313.pyc | Bin 1246 -> 1246 bytes __pycache__/database.cpython-313.pyc | Bin 15726 -> 15726 bytes .../checkpoint_migration.json | 1 + .../__pycache__/ml_mapper.cpython-313.pyc | Bin 18223 -> 18223 bytes .../__pycache__/transformers.cpython-313.pyc | Bin 12768 -> 12768 bytes .../migration_engine.cpython-313.pyc | Bin 43589 -> 43589 bytes 6 files changed, 1 insertion(+) create mode 100644 migration_checkpoints/checkpoint_migration.json diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc index 28c900bfc46ac2a638b3bf233c22a96db19be8a7..977ff42a95d28c6ee66b5ed1cf6390b6ab3b4822 100644 GIT binary patch delta 19 Zcmcb|d5@FpGcPX}0}$+*zme+-3jjP71>67t delta 19 Zcmcb|d5@FpGcPX}0}$MqwUO%z3jjRG1^EB~ diff --git a/__pycache__/database.cpython-313.pyc b/__pycache__/database.cpython-313.pyc index 81768095383880ae2dea4635fdd02b1716c2cc60..adee875f8ecd679729d1f7e1a43d9d4d407182fb 100644 GIT binary patch delta 19 ZcmaD?^{$HRGcPX}0}$+*zmY4&7644J2B!c3 delta 19 ZcmaD?^{$HRGcPX}0}$MqwUH~u7646S2E+gW diff --git a/migration_checkpoints/checkpoint_migration.json b/migration_checkpoints/checkpoint_migration.json new file mode 100644 index 0000000..44db8d7 --- /dev/null +++ b/migration_checkpoints/checkpoint_migration.json @@ -0,0 +1 @@ +{"config_name": "migration", "last_batch": 42, "rows_processed": 42000, "timestamp": "2026-02-04T09:25:10.604631"} \ No newline at end of file diff --git a/services/__pycache__/ml_mapper.cpython-313.pyc b/services/__pycache__/ml_mapper.cpython-313.pyc index dfec9a0d88549e00c78ae59525a2f148f560b2e9..0198e5f4d418e2befe57914f98d57328c06d0dba 100644 GIT binary patch delta 21 bcmZ4A$GE Date: Wed, 4 Feb 2026 11:23:25 +0700 Subject: [PATCH 3/3] refactor(migration_engine, db_connector): enhance SQL Server handling and charset options --- .../checkpoint_migration.json | 1 - .../__pycache__/db_connector.cpython-313.pyc | Bin 18017 -> 18130 bytes .../__pycache__/transformers.cpython-313.pyc | Bin 12768 -> 12768 bytes services/db_connector.py | 11 ++- .../migration_engine.cpython-313.pyc | Bin 43589 -> 45062 bytes views/migration_engine.py | 65 ++++++++++++++++-- 6 files changed, 69 insertions(+), 8 deletions(-) delete mode 100644 migration_checkpoints/checkpoint_migration.json diff --git a/migration_checkpoints/checkpoint_migration.json b/migration_checkpoints/checkpoint_migration.json deleted file mode 100644 index 44db8d7..0000000 --- a/migration_checkpoints/checkpoint_migration.json +++ /dev/null @@ -1 +0,0 @@ -{"config_name": "migration", "last_batch": 42, "rows_processed": 42000, "timestamp": "2026-02-04T09:25:10.604631"} \ No newline at end of file diff --git a/services/__pycache__/db_connector.cpython-313.pyc b/services/__pycache__/db_connector.cpython-313.pyc index 7d7e475533b77e30baae8aa446aaa1dd47975082..bb0fb77dfc351c89a12f7ad3c81181d440a4b846 100644 GIT binary patch delta 2296 zcma)7Z){Ul6o0qvdtK?@0d2LE(!u^vz^yPww=o8jGGUB?lqMr=HgD}4D{EiNeH}Q+ zU}#82gs^Zw;6%bqOpGSRk>@uP zO`Z1rpmXWER@AMqC)`PUl*o>xknn5<+1@Uy`&W`6NkNw~*Bs z2u8-labYg8G--yFWldJuw%2vYZrEVImpRQ@FTikLN_lvOruh-!Sg*cjWW z2|2NpmDisz$7-XRy_8W>$0Vv`3-SqPUQv$b))t5LZ+3!b=lL_l&0GQvIgSoVbU0NM zshC$ZL6Q$=Wl1BF;)Wn$c2PLCWIad@_$m8<-Y9o}aT{TN>-%f}DC1VwO}sGGKDOs> zuxfH-dRfC*=Y4_m_@@K0uide4YU{@xulLS)*8af0u!c;&m2B2}(7QEm;%=H2#VgD= z9iDj5d^5<$D^#(pnvh;~%(k77diMFEYI_ty(q;fH#ELebtu~dCrQ@lT#DexFZxvb> zBP>CvMyOGb*!`q#K@-@uvO#G$l@Uv#BEU4P4;p1tjz)5X-E(x2R<+UjDG}PySp(zU ztqX@?-R>F^Wuxw9QdB>3e?mwHJ6RrH)rtGM5OfM1ASo6A3`F(g>|!66N6A_BxAL@^ z)Ubs2B;c1`KRL*L@^;#>)+km5?Pje$(Gx;(1wt5MCHug)Y0p6rBTlLZd(u%2Cs?NR zB>>HlF37T!E~P|zSfL{7`w+UASm_gVYP(sNzm|-vBmT>#oiQ**%+$CDTzCK#)~5vB z1B(A!9J#3rjRhU!jnryKwbx zgnooU*1UXOpanz)TW*_?oq9AO^s1xF{X}Th=jzo^J=ur8bh>mEN|>35&B*ioDnvUb zQ6njeH9+eC6dM3uiay|G!r^E5lc4)n-4ecL&h+3yJobUEcdc|7XL`u}AZca&QYk$k z=5q5HIG@5mP|^cZUZf|`UkBS(876g@a;V;yE5rO*Q2(f2t?aXqkxP+F

9(@!)CD zMqCSfW?R>IH=jXohD(%Q1<cM#*eBjs@*97?sVE9{|T4NT- z82d2VKu)PYM`!udb5Bi2VVNd~#iAr<<{$hC^k9ck`g192PAcK+Sdf*0Y*C@`{2H|9 zu!TJShprs+@t0wBxq2@4tYscU&7P5zG|d*#vrczD&GLnB@e$3}K+^bJHlHogd6aD> zu`eg3sMN31)hrOpm8R!$hlLi7E3}T)wFDrO9WBfFw?W;l9%-302PV+_8wghrd8Xo<|R6b?&k8PZ=vqri_PNnA;Vj3A^sguDC#e58%?M6 zm%wBcIksbjf8sSMJlOTD7%i%IFTzCv?v{cu%6(^9ddE^VW2u@vJaynk{I+Em#m-H` z@6o`{!WUz99sHYLi`NDp=P(Ec+KZrfe3adbFI)K@%r9XOEaL}3LNk`oZA;i#7Gu>r zHjy^gyJN$P_!p_a9LAxruPHVc#ZsqOKom1X@e+&|2;Dy(6Q){OOTLg9%1N#CI=j20 e%G*JRXSyddEe+1}tv~RZ{&wl`ok>-8? delta 2177 zcma)7Yitx%6rQ^?vk!J>cU!uetaS3mdPn0E{jU-`jvlSfO?)^^F z&T)xyww$l$T}c<~=6%i5{<3My!ldNpnk_BF)P%UCq>uzDNS#t2>Ek;1#atf=S7_t> zUnFo;%B$+(Tx4*>_|KjbIgYwHHYL77+$>?G9;;I5&lVJ&D5@JgvLCI(wvhe%KxpErNq-~D*!TLHHn(usS{B4A_-jvlV;=sx zM~qcyH|)y@>CygnY{sN@IF~si;FGoh=r!!F!|#ZuQU&EmD%HzQJKKDa5DI=+jT#{8QOhd+?5mqa$B{JJ5?Idm5Md@o|?L=pdY}m75{xIwt z&vFuB_dM%JzqZ1Am5@05r7Tpp1@~=3Fet=9QUw4EM2+Lb*vj%oGO9gap5{p%yY4#% zxX~XVJ#3GEhqDg%;^olK@=+yst{^eD6l5m17cXBMzBX6)pW8HH|+(`UFl+> zprlJFnf9x+7xf1akgO^JAjRona_wSQ15t8H`!z6b+1dna!aOxIgFCxXVS5aVt*}V7 z0X%RG$^JIDfqbU9D%;3v!>2$S!LD0}<=l{>8g6wlol|5QwwT@xj?e?(1on?qHIl3B z!>Sb7!xmJ>yv<-RTu>gRAbYMl*lbjdOD)NzY&x%$28tPz`F0E?94r=dG={6Y5M+ct z_EGhUU@M3!0oZ7!Ac`i09_?OrfLPm%x#nKnOm?9!gDzc+5?)o)vP3)Hs!mcA zi-I-)s1AS!c`;7*R%nqp47y3})6fMz^DHjJWB2U5Xrm>Z86lqmNw@bYrSyQD%gtpV zJ%)i*O%Et}nGT`9IJ>emw8$VcPYtl2mxjbKSa(Nr*B=zfi|lat3>jfU!<$t{K^=C_ zAB$aX@U0(3|7H-BUIx%@>0)juUr^~ws4~LhBQl)>L!gl}kt*?3(EqO0MtCbZ!RlAF zkP&VFs{7*c*=eB1(5+P-98?OKxtVwbI}B}f*ldQnM3o$rOR=C1WCvC1!*$4d7FoF1 zxop`Ft$y(gEVOI?v_2v9&t|AQGm4s~*+KMd(49-OZ2lxp(>w{1F6OfNY>CdG%#OAP zU8m3{(Bcu5Hn55IAiPOmv{#B3K;5m~ZlBNSi;ql* z3ZEl0|7Ld@jf-%SKs!}{Fvi^xDsBn&Q$qbj|K*-9V>g9u6q_~;zv+QZg^$I3_;N9C zJX$rALmwC@e5pBOA3Qkw;+0E2fca|}1nc!dkeVr>=B5xbmo>5R_!`pAzKb{Q#_yr= zXqbn>mZoC}Skx3th2jlSOoI9Np!=CIVXE8P^To_iPT4>|X1lxUeVv4OCwnuK%Au*= S+#eiH|J=o1@7gTj`Thm961%(r diff --git a/services/__pycache__/transformers.cpython-313.pyc b/services/__pycache__/transformers.cpython-313.pyc index d9c62a3be0f94a549b647e7c71b85ee579c20502..386a63601019671b8072c7b4f79dcc091bfe606a 100644 GIT binary patch delta 20 acmaEm{2-b8GcPX}0}wduXxhkq)erzpUIyg= delta 20 acmaEm{2-b8GcPX}0}$+*-?)+csv!VS83#oG diff --git a/services/db_connector.py b/services/db_connector.py index 8715dbf..cfecb09 100644 --- a/services/db_connector.py +++ b/services/db_connector.py @@ -54,7 +54,16 @@ def create_sqlalchemy_engine(db_type, host, port, db_name, user, password, chars elif db_type == "Microsoft SQL Server": # Requires: pip install pymssql + # For Thai data: use 'utf8' or 'cp874' (Thai Windows codepage) + # If source contains legacy TIS-620, try 'cp874' charset mssql_charset = charset if charset else "utf8" + + query_params = {"charset": mssql_charset} + + # For legacy Thai databases, add TDS version for better compatibility + if charset in ['tis620', 'cp874', 'latin1']: + query_params["tds_version"] = "7.0" # Compatible with older SQL Server + connection_url = URL.create( "mssql+pymssql", username=user, @@ -62,7 +71,7 @@ def create_sqlalchemy_engine(db_type, host, port, db_name, user, password, chars host=host, port=port_int or 1433, database=db_name, - query={"charset": mssql_charset} + query=query_params ) else: diff --git a/views/__pycache__/migration_engine.cpython-313.pyc b/views/__pycache__/migration_engine.cpython-313.pyc index f4e6de1cc6172089be12738c1ccc4ee548b77a7a..8865a5849f5d50e6f0e5e5ab0eeabf66118c48df 100644 GIT binary patch delta 9681 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 delta 8373 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 HHemQ~mtxA= diff --git a/views/migration_engine.py b/views/migration_engine.py index 549b0be..c6bc7a5 100644 --- a/views/migration_engine.py +++ b/views/migration_engine.py @@ -54,6 +54,7 @@ def generate_select_query(config_data, source_table, db_type='MySQL'): """ Generate a SELECT query based on configuration. Applies TRIM at source for MSSQL CHAR columns to prevent padding. + For SQL Server: Also cleans non-breaking spaces and control characters at source. """ try: if not config_data or 'mappings' not in config_data: @@ -65,8 +66,23 @@ def generate_select_query(config_data, source_table, db_type='MySQL'): continue source_col = mapping['source'] - # Apply TRIM at source for MSSQL to handle CHAR padding - if db_type == 'Microsoft SQL Server' and 'TRIM' in mapping.get('transformers', []): + + # Special handling for SQL Server text columns + if db_type == 'Microsoft SQL Server': + col_expr = f'"{source_col}"' + + # Apply TRIM if specified in transformers + if 'TRIM' in mapping.get('transformers', []): + col_expr = f'TRIM({col_expr})' + + # Clean non-breaking spaces and problematic characters for VARCHAR/NVARCHAR/TEXT columns + # REPLACE(col, CHAR(160), ' ') -> replace nbsp with regular space + # REPLACE(col, CHAR(0), '') -> remove null bytes + col_expr = f'REPLACE(REPLACE({col_expr}, CHAR(160), \' \'), CHAR(0), \'\')' + + selected_cols.append(f'{col_expr} AS "{source_col}"') + elif 'TRIM' in mapping.get('transformers', []): + # Other databases: just apply TRIM if needed selected_cols.append(f'TRIM("{source_col}") AS "{source_col}"') else: selected_cols.append(f'"{source_col}"') @@ -293,19 +309,56 @@ def render_migration_engine_page(): src_sel = st.selectbox("Source Profile", ds_options, key="src_sel") st.session_state.migration_src_profile = src_sel - charset_options = ["utf8mb4 (Default)", "tis620 (Thai Legacy)", "latin1 (Raw Bytes)"] + # Get source DB type to show appropriate charset options + src_db_type = None + if src_sel != "Select Profile...": + row = datasources[datasources['name'] == src_sel].iloc[0] + ds_detail = db.get_datasource_by_id(int(row['id'])) + src_db_type = ds_detail['db_type'] + + # Show charset options based on DB type + if src_db_type == 'Microsoft SQL Server': + charset_options = [ + "utf8 (Default - Modern)", + "cp874 (Thai Windows Codepage - แนะนำสำหรับข้อมูลไทยเก่า)", + "latin1 (Raw Bytes)" + ] + help_text = "SQL Server: ใช้ cp874 สำหรับข้อมูลไทยแบบเก่า" + elif src_db_type == 'MySQL': + charset_options = [ + "utf8mb4 (Default)", + "tis620 (Thai Legacy)", + "latin1 (Raw Bytes)" + ] + help_text = "MySQL: ใช้ tis620 ถ้าภาษาไทยเพี้ยน" + else: + charset_options = [ + "utf8 (Default)", + "latin1 (Raw Bytes)" + ] + help_text = "เลือก charset ตามฐานข้อมูลต้นทาง" + src_charset_sel = st.selectbox( - "Source Charset (ถ้าภาษาไทยเพี้ยนให้ลอง tis620)", - charset_options, - key="src_charset_sel" + "Source Charset", + charset_options, + key="src_charset_sel", + help=help_text ) + + # Map selection to actual charset value charset_map = { "utf8mb4 (Default)": None, + "utf8 (Default - Modern)": None, + "utf8 (Default)": None, "tis620 (Thai Legacy)": "tis620", + "cp874 (Thai Windows Codepage - แนะนำสำหรับข้อมูลไทยเก่า)": "cp874", "latin1 (Raw Bytes)": "latin1" } st.session_state.src_charset = charset_map.get(src_charset_sel) + if src_charset_sel.startswith("cp874"): + st.info("💡 **cp874** จะแก้ปัญหา non-breaking space และตัวอักษรไทยเก่าใน SQL Server") + if src_sel != "Select Profile...": if st.button("🔍 Test Source"): with st.spinner("Connecting..."):