fix: DT ve arşiv kayıtları sektör alanına ihale türü yazıyordu — sektör filtresi bu kayıtları bulamıyordu
This commit is contained in:
+3
-1
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
import { sektorBul } from './siniflandir.mjs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { titleCaseTr } from './tr-text.mjs';
|
import { titleCaseTr } from './tr-text.mjs';
|
||||||
@@ -73,7 +74,8 @@ function mapDtRow(row) {
|
|||||||
saat: '',
|
saat: '',
|
||||||
alimTuru: 'Doğrudan Temin',
|
alimTuru: 'Doğrudan Temin',
|
||||||
usul: det.yasaMaddesi ? `Doğrudan Temin (4734 ${det.yasaMaddesi})` : 'Doğrudan Temin (4734 22/d)',
|
usul: det.yasaMaddesi ? `Doğrudan Temin (4734 ${det.yasaMaddesi})` : 'Doğrudan Temin (4734 22/d)',
|
||||||
sektor: row.turu || 'Genel',
|
// Sektör ihale TÜRÜ değildir; başlık + kalem adlarından çıkarılır.
|
||||||
|
sektor: sektorBul(row.isin_adi || '', (det.bransKodlari || []).join(' '), row.turu || ''),
|
||||||
brans: (det.bransKodlari || []).join(', ') || row.turu || 'Genel',
|
brans: (det.bransKodlari || []).join(', ') || row.turu || 'Genel',
|
||||||
kurumTipi: classifyKurum(kurum),
|
kurumTipi: classifyKurum(kurum),
|
||||||
eklenmeZamani: row.yayim_tarihi ? `Yayım: ${row.yayim_tarihi}` : '',
|
eklenmeZamani: row.yayim_tarihi ? `Yayım: ${row.yayim_tarihi}` : '',
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
|
import { sektorBul } from './siniflandir.mjs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { titleCaseTr } from './tr-text.mjs';
|
import { titleCaseTr } from './tr-text.mjs';
|
||||||
@@ -163,7 +164,9 @@ function rowToTender(r) {
|
|||||||
sehir: titleCaseTr(r.il),
|
sehir: titleCaseTr(r.il),
|
||||||
tarih: r.ihale_tarihi || '',
|
tarih: r.ihale_tarihi || '',
|
||||||
alimTuru: r.ihale_tipi || 'İhale',
|
alimTuru: r.ihale_tipi || 'İhale',
|
||||||
sektor: r.ihale_tipi || 'Genel',
|
// Sektör ihale TÜRÜ değildir ('Mal Alımı' bir sektör değil);
|
||||||
|
// ihale adından ve kalem adlarından çıkarılır.
|
||||||
|
sektor: sektorBul(r.ihale_adi || '', kalemAdlari(r.okas_kalemler)),
|
||||||
kazananFirma: r.kazanan_firma,
|
kazananFirma: r.kazanan_firma,
|
||||||
sozlesmeBedeli: r.sozlesme_bedeli,
|
sozlesmeBedeli: r.sozlesme_bedeli,
|
||||||
paraBirimi: r.para_birimi,
|
paraBirimi: r.para_birimi,
|
||||||
@@ -190,6 +193,17 @@ function rowToTender(r) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OKAS kalemlerinden ad listesi — sektör tahminini güçlendirir.
|
||||||
|
function kalemAdlari(ham) {
|
||||||
|
if (!ham) return '';
|
||||||
|
try {
|
||||||
|
const k = JSON.parse(ham);
|
||||||
|
return Array.isArray(k) ? k.map((x) => x?.ad || '').join(' ') : '';
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function readHistory(limit = 500, { firma, idare, il } = {}) {
|
export function readHistory(limit = 500, { firma, idare, il } = {}) {
|
||||||
const db = openDb();
|
const db = openDb();
|
||||||
if (!db) return { tenders: [], totalCount: 0, available: false };
|
if (!db) return { tenders: [], totalCount: 0, available: false };
|
||||||
|
|||||||
+1
-56
@@ -17,6 +17,7 @@ import crypto from 'node:crypto';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { readDtTenders } from './dt-store.mjs';
|
import { readDtTenders } from './dt-store.mjs';
|
||||||
|
import { SECTOR_RULES, INSTITUTION_RULES, classify, sektorBul, SEKTOR_ETIKETLERI, KURUM_ETIKETLERI } from './siniflandir.mjs';
|
||||||
import { loginWithGoogle, userFromAuthHeader } from './auth.mjs';
|
import { loginWithGoogle, userFromAuthHeader } from './auth.mjs';
|
||||||
import { emailConfigured, sendEmail, tenderDigestHtml, emailFrom } from './email.mjs';
|
import { emailConfigured, sendEmail, tenderDigestHtml, emailFrom } from './email.mjs';
|
||||||
import { aiConfigured, availableProviders, analyzeSpec } from './ai.mjs';
|
import { aiConfigured, availableProviders, analyzeSpec } from './ai.mjs';
|
||||||
@@ -79,62 +80,6 @@ for (const [region, cities] of Object.entries(REGIONS)) {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Basit sektör / kurum tipi sınıflandırması (başlık ve kurum adına göre)
|
// Basit sektör / kurum tipi sınıflandırması (başlık ve kurum adına göre)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Sektör sınıflandırması — 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" → Elektrik & Enerji).
|
|
||||||
// UYARI: buradaki etiketler src/data/constants.js SECTORS_LIST ile birebir
|
|
||||||
// aynı olmalı. Sürüklenmeyi önlemek için liste /api/meta/sektorler ile sunulur.
|
|
||||||
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'],
|
|
||||||
[/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'],
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
const INSTITUTION_RULES = [
|
|
||||||
[/büyükşehir 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'],
|
|
||||||
[/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'],
|
|
||||||
];
|
|
||||||
|
|
||||||
// Arayüzün kullandığı sektör listesi buradan türetilir — elle tutulan ikinci
|
|
||||||
// bir liste tutmayız, böylece iki taraf birbirinden ayrı düşemez.
|
|
||||||
const SEKTOR_ETIKETLERI = [...new Set(SECTOR_RULES.map(([, ad]) => ad))].sort((a, b) =>
|
|
||||||
a.localeCompare(b, 'tr'));
|
|
||||||
const KURUM_ETIKETLERI = [...new Set(INSTITUTION_RULES.map(([, ad]) => ad))].sort((a, b) =>
|
|
||||||
a.localeCompare(b, 'tr'));
|
|
||||||
|
|
||||||
function classify(rules, text, fallback) {
|
|
||||||
for (const [re, label] of rules) if (re.test(text)) return label;
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function titleCaseTr(s) {
|
function titleCaseTr(s) {
|
||||||
if (!s) return '';
|
if (!s) return '';
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Sektör ve kurum tipi sınıflandırması — TEK kaynak.
|
||||||
|
*
|
||||||
|
* Üç ayrı veri kaynağı (ilan.gov.tr canlı akışı, doğrudan temin, EKAP sonuç
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
|
||||||
|
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'],
|
||||||
|
[/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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
export const INSTITUTION_RULES = [
|
||||||
|
[/büyükşehir 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'],
|
||||||
|
[/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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
export function classify(rules, text, fallback) {
|
||||||
|
for (const [re, label] of rules) if (re.test(text)) return label;
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** İhale metninden sektör. Kaynak ne olursa olsun aynı sonucu verir. */
|
||||||
|
export function sektorBul(...parcalar) {
|
||||||
|
return classify(SECTOR_RULES, parcalar.filter(Boolean).join(' '), 'Genel');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function kurumTipiBul(ad) {
|
||||||
|
return classify(INSTITUTION_RULES, ad || '', 'Diğer Kamu Kurumları');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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'));
|
||||||
|
export const KURUM_ETIKETLERI = [...new Set(INSTITUTION_RULES.map(([, ad]) => ad))]
|
||||||
|
.sort((a, b) => a.localeCompare(b, 'tr'));
|
||||||
Reference in New Issue
Block a user