From 5c67429caa40c57530a4c14d82c11c09037516d7 Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 30 Aug 2026 01:15:15 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20T=C3=BCrk=C3=A7e=20b=C3=BCy=C3=BCk=20har?= =?UTF-8?q?f=20(=C4=B0/I)=20regexleri=20bozuyordu=20=E2=80=94=20s=C4=B1n?= =?UTF-8?q?=C4=B1fland=C4=B1r=C4=B1lmayan=20ihale=20%51'den=20%23'e=20d?= =?UTF-8?q?=C3=BC=C5=9Ft=C3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/siniflandir.mjs | 102 +++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/server/siniflandir.mjs b/server/siniflandir.mjs index 0c89dfd..73bc9cf 100644 --- a/server/siniflandir.mjs +++ b/server/siniflandir.mjs @@ -5,51 +5,79 @@ * arşivi) aynı kurallardan geçer. Aksi halde "Sağlık" filtresi yalnızca bir * kaynağı süzer, diğer iki kaynaktaki ihaleler görünmez olur. * - * Sıra ÖNEMLİ: ilk eşleşen kazanır, bu yüzden dar/özel kalıplar geniş - * olanlardan önce gelir (ör. "tıbbi gaz" → Sağlık, "doğalgaz" → Enerji). + * TÜRKÇE BÜYÜK HARF TUZAĞI: ilan başlıklarının yarısı TAMAMEN BÜYÜK HARF ve + * JavaScript'in `i` bayrağı Türkçe İ/I çiftini katlamaz — /inşaat/i, "İNŞAAT" + * ile EŞLEŞMEZ. Bu yüzden metni önce normalleştiriyoruz (Türkçe küçük harf + + * ASCII katlama) ve kuralları da normalleştirilmiş alfabede yazıyoruz: + * desenlerde ı/i/ş/ç/ğ/ö/ü YOKTUR, hepsi ASCII karşılığıdır. */ +const TR_KATLAMA = { + 'İ': 'i', 'I': 'i', 'ı': 'i', 'i': 'i', + 'Ş': 's', 'ş': 's', 'Ç': 'c', 'ç': 'c', + 'Ğ': 'g', 'ğ': 'g', 'Ö': 'o', 'ö': 'o', + 'Ü': 'u', 'ü': 'u', 'Â': 'a', 'â': 'a', 'Î': 'i', 'î': 'i', 'Û': 'u', 'û': 'u', +}; + +/** Türkçe duyarlı küçültme + ASCII katlama. "İNŞAAT" → "insaat" */ +export function normalizeTr(s) { + if (!s) return ''; + let out = ''; + for (const ch of String(s)) { + out += TR_KATLAMA[ch] ?? ch.toLowerCase(); + } + return out.replace(/\s+/g, ' '); +} + +// Desenler NORMALLEŞTİRİLMİŞ metne uygulanır: hepsi küçük harf ve ASCII. +// Sıra ÖNEMLİ: ilk eşleşen kazanır, dar kalıplar geniş olanlardan önce gelir +// (ör. "tibbi gaz" → Sağlık, "dogalgaz" → Isıtma). export const SECTOR_RULES = [ - [/tıbbi|medikal|sağlık|hastane|ilaç|cerrahi|laboratuvar|diş |protez|ortez|görüntüleme|röntgen|tomografi|steril|enjektör|eldiven|maske|reaktif|kit alım/i, 'Sağlık / Tıbbi Malzeme'], - [/yazılım|bilişim|sunucu|donanım|network|bilgisayar|lisans|veri merkezi|siber|yazıcı alım|tablet|projeksiyon/i, 'Bilişim & Yazılım'], - [/güvenlik hizmet|özel güvenlik|kamera|cctv|alarm|turnike|x-ray|güvenlik sistem/i, 'Güvenlik Sistemleri'], - [/gıda|yemek|catering|ekmek|et alım|sebze|meyve|kumanya|süt|tavuk|bakliyat|mutfak/i, 'Gıda & Catering'], - [/temizlik|hijyen|dezenfek|deterjan|çöp|atık toplama|haşere|ilaçlama/i, 'Temizlik & Hijyen'], - [/asfalt|beton|yol yapım|köprü|altyapı|kanalizasyon|içme suyu|sulama|menfez|istinat/i, 'Altyapı & Yol'], - [/inşaat|yapım işi|onarım|tadilat|güçlendirme|çatı|cephe|restorasyon|bakım onarım/i, 'İnşaat & Bakım'], - [/kırtasiye|matbaa|baskı|kağıt|toner|kartuş|fotokopi|büro malzeme/i, 'Kırtasiye & Büro'], - [/mobilya|masa|sandalye|dolap|okul sırası|büro mobilya|raf /i, 'Mobilya & Donatı'], - [/giyim|tekstil|elbise|üniforma|kıyafet|ayakkabı|çamaşır|kumaş|battaniye/i, 'Tekstil & Giyim'], - [/akaryakıt|motorin|benzin|lpg|yakıt alım/i, 'Akaryakıt & Yakıt'], - [/araç kirala|taşıt kirala|servis hizmet|personel taşıma|öğrenci taşıma|nakliye|lojistik/i, 'Taşımacılık & Kiralama'], - [/araç|otomotiv|lastik|yedek parça|otobüs|kamyon|iş makinesi|greyder|ekskavatör/i, 'Otomotiv & İş Makinesi'], - [/elektrik|trafo|enerji|aydınlatma|jeneratör|güneş|solar|kablo|pano |ups /i, 'Elektrik & Enerji'], - [/doğalgaz|kalorifer|kazan|ısıtma|soğutma|klima|havalandırma|iklimlendirme|mekanik tesisat/i, 'Isıtma & İklimlendirme'], - [/asansör|yürüyen merdiven/i, 'Asansör & Yürüyen Sistem'], - [/su arıtma|arıtma tesis|pompa|sondaj|kuyu |su deposu/i, 'Su & Arıtma'], - [/tarım|tohum|gübre|fide|zirai|hayvan|yem |veteriner|sera /i, 'Tarım & Hayvancılık'], - [/peyzaj|park bahçe|ağaç|çim |fidan|oyun grubu|kent mobilya/i, 'Peyzaj & Park'], - [/eğitim hizmet|kurs|seminer|danışmanlık|müşavirlik|etüt proje|mühendislik hizmet|kontrollük/i, 'Danışmanlık & Proje'], - [/reklam|tanıtım|organizasyon|fuar|basın|medya|prodüksiyon/i, 'Reklam & Organizasyon'], + [/tibbi|medikal|saglik|hastane|ilac |poliklinik|cerrahi|ameliyat|diyaliz|radyoloji|rontgen|tomografi|ultrason|steril|enjektor|eldiven|maske|pansuman|ped |sonda|kateter|protez|ortez|isitme cihaz|dis unit|endo motor|reaktif|hemogram|serum|asi |sedye|ligasyon|test kiti| kit alimi|manometre|oksijen regulator|medikal gaz|isitme engelli|induksiyon dongu|hasta yatak|tekerlekli sandalye|otoklav|aspirator|nebulizator|puls oksimetre/i, 'Sağlık / Tıbbi Malzeme'], + [/yazilim|bilisim|sunucu|donanim|network|bilgisayar|lisans|veri merkezi|siber|yazici alim|toner alim|tablet|projeksiyon|kart okuyucu|nitelikli sertifika|e-imza sertifika|barkod|e-imza|otomasyon sistem/i, 'Bilişim & Yazılım'], + [/guvenlik hizmet|ozel guvenlik|kamera|cctv|alarm|turnike|x-ray|guvenlik sistem|yangin sondur|yangin tup|yangin algila|itfaiye/i, 'Güvenlik Sistemleri'], + [/gida|yemek|catering|ekmek|et alim|kirmizi et|beyaz et|sebze|meyve|kumanya|sut |peynir|tavuk|bakliyat|mutfak|gazoz|icecek|su alimi|cips|kuru gida|dana |kuzu |fritoz|reso |benmari|bulasik makinesi|mutfak ekipman|kantin/i, 'Gıda & Catering'], + [/temizlik|hijyen|dezenfek|deterjan|cop |atik toplama|hasere|ilaclama|supurge|kagit havlu|tuvalet kagidi|konteyner|atik depo|geri donusum/i, 'Temizlik & Hijyen'], + [/asfalt|beton|yol yapim|kilit parke|bordur|kaldirim|kopru|altyapi|kanalizasyon|icme suyu|sulama|menfez|istinat|stabilize|dolgu/i, 'Altyapı & Yol'], + [/insaat|yapim isi|onarim|tamirat|tadilat|guclendirme|cati|cephe|restorasyon|bakim onarim|boya |badana|dograma|pvc pencere|hafriyat|yikim|nalbur|hirdavat|civata|vida |cimento|demir alim|kereste|fayans|seramik|izolasyon|mantolama/i, 'İnşaat & Bakım'], + [/kirtasiye|matbaa|baski|kagit|toner|kartus|fotokopi|buro malzeme|defter|album|dosya alim|zarf |basili evrak|form basim|kaset|klasor/i, 'Kırtasiye & Büro'], + [/mobilya|masa |sandalye|dolap|okul sirasi|buro mobilya|raf |perde|koltuk|yatak |karyola/i, 'Mobilya & Donatı'], + [/giyim|tekstil|elbise|uniforma|kiyafet|ayakkabi|bot alim|camasir|kumas|battaniye|havlu |nevresim/i, 'Tekstil & Giyim'], + [/akaryakit|motorin|benzin|lpg|yakit alim|odun |komur|pelet /i, 'Akaryakıt & Yakıt'], + [/arac kirala|tasit kirala|servis hizmet|personel tasima|ogrenci tasima|nakliye|lojistik|treyler|cekici kirala/i, 'Taşımacılık & Kiralama'], + [/arac |otomotiv|lastik|yedek parca|otobus|kamyon|is makinesi|greyder|ekskavator|kepce|traktor|transmisyon|motor yagi|aku |plakali|tasit bakim|arac bakim|balata|amortisor|rot |far alim/i, 'Otomotiv & İş Makinesi'], + [/elektrik|trafo|enerji|aydinlatma|jenerator|gunes|solar|kablo|pano |ups |armatur|ampul|led /i, 'Elektrik & Enerji'], + [/dogalgaz|kalorifer|kazan |isitma|sogutma|klima|havalandirma|iklimlendirme|mekanik tesisat|radyator|buzdolabi|sogutucu/i, 'Isıtma & İklimlendirme'], + [/asansor|yuruyen merdiven|platform lift/i, 'Asansör & Yürüyen Sistem'], + [/su aritma|aritma tesis|pompa|sondaj|kuyu |su deposu|klorlama|dalgic|boru |vana /i, 'Su & Arıtma'], + [/tarim|tohum|gubre|fide|zirai|hayvan|yem |veteriner|sera |ilaclama zirai|orman|agaclandirma/i, 'Tarım & Hayvancılık'], + [/peyzaj|park bahce|agac |cim |fidan|oyun grubu|kent mobilya|bank alim|cicek/i, 'Peyzaj & Park'], + [/egitim hizmet|kurs |seminer|danismanlik|musavirlik|etut proje|muhendislik hizmet|kontrolluk|arastirma hizmet|saha arastirma|anket/i, 'Danışmanlık & Proje'], + [/reklam|tanitim|organizasyon|fuar |basin|medya|produksiyon|afis|brosur|davetiye|promosyon|porselen kupa|hediyelik|bayrak alim|flama/i, 'Reklam & Organizasyon'], [/sigorta|zorunlu trafik|kasko/i, 'Sigorta'], - [/kitap|yayın|kütüphane|arşiv|dijitalleştirme/i, 'Yayın & Arşiv'], - [/spor|halı saha|tribün|fitness|kondisyon/i, 'Spor Tesisleri'], - [/laboratuvar cihaz|analiz cihaz|ölçüm cihaz|test cihaz/i, 'Laboratuvar & Ölçüm'], + [/kitap|yayin|kutuphane|arsiv|dijitallestirme|tercume|ceviri/i, 'Yayın & Arşiv'], + [/spor |hali saha|tribun|fitness|kondisyon|stadyum|yuzme havuz/i, 'Spor Tesisleri'], + [/laboratuvar|analiz cihaz|olcum cihaz|test cihaz|mikroskop|santrifuj|numune|kantar|tarti |terazi|kalibrasyon|sayac /i, 'Laboratuvar & Ölçüm'], + [/tasinmaz|parsel|arsa |gayrimenkul|kiraya veril|satis ihalesi|irtifak|ecrimisil/i, 'Taşınmaz & Kiralama'], + [/polisaj|testere|torna|kaynak makinesi|matkap|jeneratorlu|atolye|el aleti|kompresor/i, 'Atölye & Makine Ekipmanı'], + [/personel calistir|isci calistir|hizmet alimi personel|temizlik personel|guvenlik personel|destek personel/i, 'Personel Hizmet Alımı'], ]; export const INSTITUTION_RULES = [ - [/büyükşehir belediye/i, 'Büyükşehir Belediyeleri'], + [/buyuksehir belediye/i, 'Büyükşehir Belediyeleri'], [/belediye/i, 'Belediyeler'], - [/üniversite/i, 'Üniversiteler'], - [/hastane|sağlık müdürlüğü|başhekim/i, 'Eğitim ve Araştırma Hastaneleri'], - [/ağız ve diş/i, 'Ağız ve Diş Sağlığı Merkezleri'], + [/universite/i, 'Üniversiteler'], + [/hastane|saglik mudurlugu|bashekim/i, 'Eğitim ve Araştırma Hastaneleri'], + [/agiz ve dis/i, 'Ağız ve Diş Sağlığı Merkezleri'], [/ceza infaz|cezaevi|tevkifevi/i, 'Ceza İnfaz Kurumları'], - [/bakanlığı|bakanlık|valiliği|müdürlüğü/i, 'Bakanlıklar'], - [/a\.ş\.|genel müdürlüğü|işletme/i, 'Kamu Şirketleri'], + [/bakanligi|bakanlik|valiligi|mudurlugu/i, 'Bakanlıklar'], + [/a\.s\.|genel mudurlugu|isletme/i, 'Kamu Şirketleri'], ]; +/** Kuralları NORMALLEŞTİRİLMİŞ metne uygular. */ export function classify(rules, text, fallback) { - for (const [re, label] of rules) if (re.test(text)) return label; + const t = normalizeTr(text); + for (const [re, label] of rules) if (re.test(t)) return label; return fallback; } @@ -63,7 +91,11 @@ export function kurumTipiBul(ad) { } /** Arayüzün kullandığı listeler — kurallardan türetilir, elle tutulmaz. */ -export const SEKTOR_ETIKETLERI = [...new Set(SECTOR_RULES.map(([, ad]) => ad))] - .sort((a, b) => a.localeCompare(b, 'tr')); +// 'Genel' bir kural değil, hiçbir kalıba uymayanların düştüğü kova. Yine de +// listeye eklenir: aksi halde bu kayıtlar sektör filtresiyle hiç bulunamaz. +export const SEKTOR_ETIKETLERI = [ + ...[...new Set(SECTOR_RULES.map(([, ad]) => ad))].sort((a, b) => a.localeCompare(b, 'tr')), + 'Genel', +]; export const KURUM_ETIKETLERI = [...new Set(INSTITUTION_RULES.map(([, ad]) => ad))] .sort((a, b) => a.localeCompare(b, 'tr'));