fix: doküman sınıflandırması ilanın tamamını tarayıp yanlış sonuç üretiyordu — yalnızca işin konusu taranıyor, eski kayıtlar geçersiz
This commit is contained in:
@@ -88,6 +88,14 @@ export function openDb({ create = false } = {}) {
|
||||
}
|
||||
const db = new DatabaseSync(DB_PATH);
|
||||
db.exec(SCHEMA);
|
||||
// Geçiş: sektör zenginleştirmesi ilanın TAMAMINI tarıyordu ve standart
|
||||
// hukuki metin ("e-imza", "numune sunulması") yanlış sınıflandırma üretiyordu.
|
||||
// v2 yalnızca işin konusunu tarar; eski kayıtlar silinip yeniden hesaplanır.
|
||||
try {
|
||||
const silinen = db.prepare("DELETE FROM sektor_zengin WHERE kaynak IS NULL OR kaynak <> 'dokuman-v2'").run();
|
||||
if (silinen?.changes) console.log(`[history-store] ${silinen.changes} eski sektör kaydı geçersiz kılındı (v2)`);
|
||||
} catch { /* tablo henüz yoksa sorun değil */ }
|
||||
|
||||
// Geçiş: eski şemada okas_kalemler yoksa ekle
|
||||
try {
|
||||
const kolonlar = db.prepare('PRAGMA table_info(sonuclar)').all().map((c) => c.name);
|
||||
|
||||
+7
-4
@@ -17,7 +17,7 @@ import crypto from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { readDtTenders } from './dt-store.mjs';
|
||||
import { SECTOR_RULES, INSTITUTION_RULES, classify, sektorBul, SEKTOR_ETIKETLERI, KURUM_ETIKETLERI } from './siniflandir.mjs';
|
||||
import { SECTOR_RULES, INSTITUTION_RULES, classify, sektorBul, SEKTOR_ETIKETLERI, KURUM_ETIKETLERI, ilanKonusu } from './siniflandir.mjs';
|
||||
import { loginWithGoogle, userFromAuthHeader } from './auth.mjs';
|
||||
import { emailConfigured, sendEmail, tenderDigestHtml, emailFrom } from './email.mjs';
|
||||
import { aiConfigured, availableProviders, analyzeSpec } from './ai.mjs';
|
||||
@@ -334,11 +334,14 @@ async function sektorZenginlestir() {
|
||||
await Promise.all(grup.map(async (t) => {
|
||||
try {
|
||||
const det = await getDetail(t.id.replace(/^ilan-/, ''));
|
||||
const sek = sektorBul(t.baslik || '', det.aciklama || '');
|
||||
sonuc.push({ id: t.id, sektor: sek });
|
||||
// İlanın TAMAMI değil, yalnızca işin konusu taranır: standart
|
||||
// hukuki metin ("e-imza ile imzalanarak", "numune sunulması")
|
||||
// sınıflandırmayı zehirliyordu.
|
||||
const sek = sektorBul(t.baslik || '', ilanKonusu(det.aciklama || ''));
|
||||
sonuc.push({ id: t.id, sektor: sek, kaynak: 'dokuman-v2' });
|
||||
} catch (e) {
|
||||
// Metin alınamadıysa 'Genel' yaz — sonsuz yeniden deneme olmasın.
|
||||
sonuc.push({ id: t.id, sektor: 'Genel' });
|
||||
sonuc.push({ id: t.id, sektor: 'Genel', kaynak: 'dokuman-v2' });
|
||||
}
|
||||
}));
|
||||
// Kaynağı yormamak için gruplar arasında kısa duraklama.
|
||||
|
||||
+34
-2
@@ -33,7 +33,7 @@ export function normalizeTr(s) {
|
||||
// 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 = [
|
||||
[/\btibbi|\bmedikal|\bsaglik|\bhastane|\bilac\b|\bpoliklinik|\bcerrahi|\bameliyat|\bdiyaliz|\bradyoloji|\brontgen|\btomografi|\bultrason|\bsteril|\benjektor|\beldiven|\bmaske|\bpansuman|\bped\b|\bsonda|\bkateter|\bprotez|\bortez|\bisitme cihaz|\bdis unit|\bendo motor|\breaktif|\bhemogram|\bserum|\basi alimi|\basilama|\bsedye|\bligasyon|\btest kiti|\bkit alimi|\bmanometre|\boksijen regulator|\bmedikal gaz|\bisitme engelli|\binduksiyon dongu|\bhasta yatak|\btekerlekli sandalye|\botoklav|\baspirator|\bnebulizator|\bpuls oksimetre/i, 'Sağlık / Tıbbi Malzeme'],
|
||||
[/\btibbi|\bmedikal|\bsaglik|\bhastane|\bilac\b|\bpoliklinik|\bcerrahi|\bameliyat|\bdiyaliz|\bradyoloji|\brontgen|\btomografi|\bultrason|\bsteril|\benjektor|\beldiven|\bmaske|\bpansuman|\bped\b|\bsonda|\bkateter|\bprotez|\bortez|\bisitme cihaz|\bdis unit|\bdental|\bdis hekim|\bimplant|\bendo motor|\breaktif|\bhemogram|\bserum|\basi alimi|\basilama|\bsedye|\bligasyon|\btest kiti|\bkit alimi|\bmanometre|\boksijen regulator|\bmedikal gaz|\bisitme engelli|\binduksiyon dongu|\bhasta yatak|\btekerlekli sandalye|\botoklav|\baspirator|\bnebulizator|\bpuls oksimetre/i, 'Sağlık / Tıbbi Malzeme'],
|
||||
[/\byazilim|\bbilisim|\bsunucu|\bdonanim|\bnetwork|\bbilgisayar|\blisans|\bveri merkezi|\bsiber|\byazici alim|\btoner alim|\btablet|\bprojeksiyon|\bkart okuyucu|\bnitelikli sertifika|\be-imza sertifika|\bbarkod|\be-imza|\botomasyon sistem/i, 'Bilişim & Yazılım'],
|
||||
[/\bguvenlik hizmet|\bozel guvenlik|\bkamera|\bcctv|\balarm|\bturnike|\bx-ray|\bguvenlik sistem|\byangin sondur|\byangin tup|\byangin algila|\bitfaiye/i, 'Güvenlik Sistemleri'],
|
||||
[/\bgida|\byemek|\bcatering|\bekmek|\bet alim|\bkirmizi et|\bbeyaz et|\bsebze|\bmeyve|\bkumanya|\bsut\b|\bpeynir|\btavuk|\bbakliyat|\bmutfak|\bgazoz|\bicecek|\bsu alimi|\bcips|\bkuru gida|\bdana\b|\bkuzu\b|\bfritoz|\breso\b|\bbenmari|\bbulasik makinesi|\bmutfak ekipman|\bkantin/i, 'Gıda & Catering'],
|
||||
@@ -50,7 +50,7 @@ export const SECTOR_RULES = [
|
||||
[/\bdogalgaz|\bkalorifer|\bkazan\b|\bisitma|\bsogutma|\bklima|\bhavalandirma|\biklimlendirme|\bmekanik tesisat|\bradyator|\bbuzdolabi|\bsogutucu/i, 'Isıtma & İklimlendirme'],
|
||||
[/\basansor|\byuruyen merdiven|\bplatform lift/i, 'Asansör & Yürüyen Sistem'],
|
||||
[/\bsu aritma|\baritma tesis|\bpompa|\bsondaj|\bkuyu\b|\bsu deposu|\bklorlama|\bdalgic|\bboru\b|\bvana\b/i, 'Su & Arıtma'],
|
||||
[/\btarim|\btohum|\bgubre|\bfide|\bzirai|\bhayvan|\byem\b|\bveteriner|\bsera\b|\bilaclama zirai|\borman|\bagaclandirma/i, 'Tarım & Hayvancılık'],
|
||||
[/\btarim|\btohum|\bgubre|\bfide|\bzirai|\bhayvan|\byem\b|\bveteriner|\bmama\b|\bkedi\b|\bkopek\b|\bevcil|\bsera\b|\bilaclama zirai|\borman|\bagaclandirma/i, 'Tarım & Hayvancılık'],
|
||||
[/\bpeyzaj|\bpark bahce|\bagac\b|\bcim\b|\bfidan|\boyun grubu|\bkent mobilya|\bbank alim|\bcicek/i, 'Peyzaj & Park'],
|
||||
[/\begitim hizmet|\bkurs\b|\bseminer|\bdanismanlik|\bmusavirlik|\betut proje|\bmuhendislik hizmet|\bkontrolluk|\barastirma hizmet|\bsaha arastirma|\banket/i, 'Danışmanlık & Proje'],
|
||||
[/\breklam|\btanitim|\borganizasyon|\bfuar\b|\bbasin|\bmedya|\bproduksiyon|\bafis|\bbrosur|\bdavetiye|\bpromosyon|\bporselen kupa|\bhediyelik|\bbayrak alim|\bflama/i, 'Reklam & Organizasyon'],
|
||||
@@ -74,6 +74,38 @@ export const INSTITUTION_RULES = [
|
||||
[/a\.s\.|\bgenel mudurlugu|\bisletme/i, 'Kamu Şirketleri'],
|
||||
];
|
||||
|
||||
/**
|
||||
* İlan metninden YALNIZCA işin konusunu ayıklar.
|
||||
*
|
||||
* İlanın tamamı standart hukuki metinle doludur ("e-imza ile imzalanarak",
|
||||
* "numune sunulması istenmektedir", "…Sağlık Bakanlığı", "lisans belgesi") ve
|
||||
* bunlar sınıflandırmayı zehirler: "Kedi ve köpek maması" ilanı e-imza
|
||||
* cümlesi yüzünden Bilişim çıkar. Bu yüzden yalnızca iki güvenilir bölgeyi
|
||||
* okuruz:
|
||||
* 1) Başlangıç → "4734 sayılı" (ilanın konu cümlesi)
|
||||
* 2) "Niteliği, türü ve miktarı" alanının değeri
|
||||
*/
|
||||
export function ilanKonusu(metin) {
|
||||
if (!metin) return '';
|
||||
// Arama da dönüş de NORMALLEŞTİRİLMİŞ metin üzerinde: "4734 sayılı" gibi
|
||||
// işaretleri Türkçe karakterden bağımsız bulabilelim.
|
||||
const t = normalizeTr(
|
||||
String(metin).replace(/\{[^}]*\}/g, ' ').replace(/ /g, ' ')
|
||||
);
|
||||
|
||||
const parcalar = [];
|
||||
|
||||
// 1) Konu cümlesi: metnin başından "4734 sayılı"ya kadar
|
||||
const kanun = t.search(/4734\s*sayili|sayili kamu ihale kanunu/i);
|
||||
parcalar.push(kanun > 0 ? t.slice(0, kanun) : t.slice(0, 300));
|
||||
|
||||
// 2) "Niteliği, türü ve miktarı" alanının değeri (bir sonraki madde numarasına kadar)
|
||||
const nitelik = t.match(/nitelig[iı][^:]{0,40}:\s*([\s\S]{0,400}?)(?=\d+\.\d|\d+-\s|$)/i);
|
||||
if (nitelik) parcalar.push(nitelik[1]);
|
||||
|
||||
return parcalar.join(' ').slice(0, 800);
|
||||
}
|
||||
|
||||
// --- OKAS (CPV) kod sınıflandırması ---------------------------------------
|
||||
// OKAS, AB'nin CPV numaralandırmasını kullanır ve kodu ihaleyi AÇAN İDARE atar.
|
||||
// Yani başlıktan tahmin etmekten çok daha güvenilir bir sinyaldir; kod varsa
|
||||
|
||||
Reference in New Issue
Block a user