Files
ihaleist/src/services/pdfText.js
T
hikmettanandClaude Fable 5.1 df89eaac33 feat: takip izleme (zeyilname + sonuc bildirimi), hazirlik gorevi, belge sure uyarisi, Telegram, OCR, yasakli sorgu
28 depo taramasindan alinan alti ozellik. Kod kopyalanmadi (depolar
lisanssiz), fikirler uygulandi.

1) Zeyilname / duzeltme ilani takibi (server/zeyilname.mjs): takipteki
   IKN'ler sunucuya yazilir; 3 saatte bir EKAP detayindan ilan sayisi ve
   ihale tarihi karsilastirilir, degisince "ne degisti" bildirimi (uygulama
   ici + e-posta + Telegram). Test: anlik kaydi eskitilince tur "yeni ilan
   yayimlandi; ihale tarihi 01.01.2026 → 08.10.2026" bildirimi uretti.
2) Sonuc bildirimi: takipteki ihale sonuclaninca "X firmasi Y'ye aldi, %Z
   kirim". Yalniz takiptekiler; kismi ihalede oran yazilmaz.
3) Ihaleden goreve (MOBIT-DURT): detayda "Hazirlik Gorevi Ac" → teslim
   tarihi ihale saati, pano "Teklif Hazirlaniyor", derin analizden gelen
   belge kontrol listesi gorevde; Kanban kartinda geri sayim rozeti.
4) Sirket belgesi sure takibi: Belge Kasasi'nda son kullanma tarihi girisi,
   dolmus / 30 gun icinde dolacak uyarisi; girise gunde bir bildirim.
   PATCH /api/me/documents/meta.
5) Telegram (server/telegram.mjs): TELEGRAM_BOT_TOKEN sunucuda; kullanici
   6 haneli kodu bota yazar, "Dogrula" → getUpdates ile chat id baglanir.
   Arayuzde adim adim kurulum rehberi (BotFather → env → Redeploy → kod).
   Ihale ozeti, zeyilname ve sonuc bildirimleri Telegram'a da gider.
6) OCR (tesseract.js, tur): PDF metin katmani bossa (taranmis) ilk 8 sayfa
   OCR. Olcum: sentetik cetvelde guven 87, ~0,3 sn/sayfa; IKN ve kalemler
   dogru okundu. Kalem havuzu ve derin analiz artik taranmis PDF'ten de
   besleniyor.
7) Rakip Radari'nda KIK yasakli sorgusu baglantisi (firma adi panoya
   kopyalanir; EKAP sorguyu API'den vermiyor).

Sinir deger hesaplayici KURULMADI: Kamu Ihale Genel Tebligi'ne ulasilamadi
(mevzuat.gov.tr / ihale.gov.tr bu makineden erisilemiyor); formul kaynaksiz
yazilmadi.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 22:58:13 +03:00

75 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* PDF'ten metin çıkarma (tarayıcı içinde, pdfjs-dist ile).
* Dinamik import edilir — ana bundle'a girmez.
*/
export async function extractPdfText(file, { maxPages = 40 } = {}) {
const pdfjs = await import('pdfjs-dist');
// Worker'ı Vite'ın bundle ettiği dosyadan al (CDN'e çıkmaz)
const workerUrl = (await import('pdfjs-dist/build/pdf.worker.min.mjs?url')).default;
pdfjs.GlobalWorkerOptions.workerSrc = workerUrl;
const buf = await file.arrayBuffer();
const doc = await pdfjs.getDocument({ data: buf }).promise;
const pages = Math.min(doc.numPages, maxPages);
const parts = [];
for (let i = 1; i <= pages; i++) {
const page = await doc.getPage(i);
const content = await page.getTextContent();
parts.push(content.items.map((it) => it.str).join(' '));
}
const text = parts.join('\n\n').replace(/[ \t]+/g, ' ').trim();
return { text, pageCount: doc.numPages, usedPages: pages };
}
// Metin dosyaları için basit okuyucu
export async function extractPlainText(file) {
const text = await file.text();
return { text: text.trim(), pageCount: 1, usedPages: 1 };
}
/**
* Taranmış (görüntü) PDF için OCR — tesseract.js, Türkçe dil paketi,
* tarayıcıda. Ölçüldü: sentetik teklif cetvelinde güven 87, sayfa başı
* ~0,3 sn (Node). Yalnızca metin katmanı boşsa devreye girer; ilk 8 sayfa.
*/
export async function ocrPdfText(file, { maxPages = 8, onProgress } = {}) {
const pdfjs = await import('pdfjs-dist');
const workerUrl = (await import('pdfjs-dist/build/pdf.worker.min.mjs?url')).default;
pdfjs.GlobalWorkerOptions.workerSrc = workerUrl;
const { createWorker } = await import('tesseract.js');
const doc = await pdfjs.getDocument({ data: await file.arrayBuffer() }).promise;
const pages = Math.min(doc.numPages, maxPages);
const worker = await createWorker('tur');
const parts = [];
try {
for (let i = 1; i <= pages; i++) {
onProgress?.({ sayfa: i, toplam: pages });
const page = await doc.getPage(i);
const viewport = page.getViewport({ scale: 2 }); // ~150 dpi: OCR için yeterli, hızlı
const canvas = document.createElement('canvas');
canvas.width = viewport.width; canvas.height = viewport.height;
await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise;
const { data } = await worker.recognize(canvas);
parts.push(data.text);
}
} finally {
await worker.terminate();
}
const text = parts.join('\n\n').replace(/[ \t]+/g, ' ').trim();
return { text, pageCount: doc.numPages, usedPages: pages, ocr: true };
}
export async function extractText(file, { ocr = true, onProgress } = {}) {
const name = (file.name || '').toLowerCase();
if (name.endsWith('.pdf') || file.type === 'application/pdf') {
const r = await extractPdfText(file);
// Metin katmanı yoksa taranmış PDF'tir — OCR'a düş
if (ocr && (r.text || '').replace(/\s/g, '').length < 100) {
try { return await ocrPdfText(file, { onProgress }); } catch { return r; }
}
return r;
}
return extractPlainText(file);
}