Marek4321's picture
Update app.py
5dec935 verified
import gradio as gr
import os
from typing import List, Dict, Tuple, Optional
import pandas as pd
import logging
from pathlib import Path
from datetime import datetime
import asyncio
from functools import partial
from file_processor import FileProcessor
from criteria_analyzer import CriteriaAnalyzer
from offer_analyzer import OfferAnalyzer
from report_generator import ReportGenerator
from cache_manager import CacheManager
from config import Config
from dotenv import load_dotenv
# Załaduj zmienne środowiskowe
load_dotenv()
# Konfiguracja logowania
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('tender_eval.log'),
logging.StreamHandler()
]
)
class TenderEvaluationApp:
"""Główna klasa aplikacji do oceny ofert przetargowych"""
def __init__(self, api_key: Optional[str] = None):
self.logger = logging.getLogger(__name__)
# Użyj przekazanego klucza API lub pobierz z konfiguracji
self.api_key = api_key or Config.get_api_config().api_key
if not self.api_key:
raise ValueError("Brak klucza API")
self.base_url = Config.get_api_config().base_url
# Utworzenie katalogów
self.temp_dir = Path(Config.TEMP_DIR)
self.output_dir = Path(Config.OUTPUT_DIR)
self.temp_dir.mkdir(exist_ok=True)
self.output_dir.mkdir(exist_ok=True)
# Inicjalizacja cache
self.cache = CacheManager(
cache_dir="cache/main",
ttl=Config.SYSTEM_CONFIG['cache_ttl'],
max_size_mb=Config.SYSTEM_CONFIG['memory_limit']
)
# Inicjalizacja komponentów
self.file_processor = FileProcessor(Config.FILE_CONFIG)
self.criteria_analyzer = CriteriaAnalyzer(
api_key=self.api_key,
base_url=self.base_url
)
self.offer_analyzer = OfferAnalyzer(
api_key=self.api_key,
base_url=self.base_url
)
self.report_generator = ReportGenerator(
output_dir=self.output_dir
)
async def process_files(
self,
brief_file: str,
offer_files: List[str],
progress = gr.Progress()
) -> Tuple[pd.DataFrame, str, str]:
"""
Główna funkcja przetwarzająca pliki i generująca analizę
Args:
brief_file: Ścieżka do pliku z briefem/SIWZ
offer_files: Lista ścieżek do plików z ofertami
progress: Obiekt do raportowania postępu
Returns:
Tuple[pd.DataFrame, str, str]: (tabela_wyników, raport_word, status)
"""
status_updates = []
try:
# 1. Przetwórz brief/SIWZ
self.logger.info(f"Rozpoczęto przetwarzanie briefu: {brief_file}")
status_updates.append("Przetwarzanie briefu...")
progress(0.1, desc="Przetwarzanie briefu")
brief_content = await self.file_processor.convert_to_text(brief_file)
# 2. Wyodrębnij kryteria z briefu
status_updates.append("Analizowanie kryteriów oceny...")
progress(0.2, desc="Analizowanie kryteriów")
async with self.criteria_analyzer:
criteria = await self.criteria_analyzer.extract_criteria(brief_content)
self.logger.info(f"Wyodrębniono {len(criteria)} kryteriów")
# 3. Przetwórz oferty
offers_content = {}
total_offers = len(offer_files)
for i, offer_file in enumerate(offer_files, 1):
offer_name = Path(offer_file).name
status_updates.append(f"Przetwarzanie oferty {i}/{total_offers}: {offer_name}")
progress(0.2 + 0.2 * (i/total_offers), desc=f"Konwersja oferty {i}/{total_offers}")
content = await self.file_processor.convert_to_text(offer_file)
offers_content[f"Oferta_{i}"] = content
# 4. Analizuj oferty
status_updates.append("Analizowanie ofert...")
progress(0.4, desc="Analiza ofert")
async with self.offer_analyzer:
analyses = await self.offer_analyzer.analyze_multiple_offers(
offers_content,
criteria,
brief_content
)
# 5. Porównaj oferty
status_updates.append("Porównywanie ofert...")
progress(0.8, desc="Porównywanie ofert")
comparison = await self.offer_analyzer.compare_offers(analyses)
# 6. Generuj raporty
status_updates.append("Generowanie raportów końcowych...")
progress(0.9, desc="Generowanie raportów")
results_df = await self.report_generator.generate_excel_report(
analyses, criteria
)
word_report = await self.report_generator.generate_word_report(
analyses, criteria, comparison
)
# 7. Zapisz wyniki
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
excel_path = self.output_dir / f"wyniki_{timestamp}.xlsx"
results_df.to_excel(excel_path, index=False)
status_updates.append("✅ Analiza zakończona!")
progress(1.0, desc="Zakończono")
return results_df, word_report, "\n".join(status_updates)
except Exception as e:
error_msg = f"❌ Błąd podczas analizy: {str(e)}"
self.logger.error(error_msg, exc_info=True)
status_updates.append(error_msg)
return None, None, "\n".join(status_updates)
async def clear_cache(self):
"""Czyści cały cache aplikacji"""
try:
await self.cache.invalidate_all()
self.logger.info("Wyczyszczono cache aplikacji")
return "✅ Cache wyczyszczony pomyślnie"
except Exception as e:
self.logger.error(f"Błąd podczas czyszczenia cache: {str(e)}")
return "❌ Błąd podczas czyszczenia cache"
def create_interface() -> gr.Blocks:
"""
Tworzy interfejs Gradio
Returns:
gr.Blocks: Interfejs aplikacji
"""
async def process_with_api_key(
api_key: str,
brief_file: str,
offer_files: List[str],
progress=gr.Progress()
) -> Tuple[pd.DataFrame, str, str]:
"""Funkcja przetwarzająca pliki z walidacją klucza API"""
try:
# Najpierw sprawdź wprowadzony klucz
if api_key:
if not Config.validate_api_key(api_key):
raise gr.Error("Nieprawidłowy format klucza API")
active_key = api_key
# Jeśli nie wprowadzono klucza, sprawdź czy jest w konfiguracji
elif Config.get_api_config().api_key:
active_key = Config.get_api_config().api_key
else:
raise gr.Error("Proszę wprowadzić klucz API")
app = TenderEvaluationApp(api_key=active_key)
return await app.process_files(brief_file, offer_files, progress)
except Exception as e:
raise gr.Error(f"Błąd: {str(e)}")
async def clear_cache():
"""Funkcja czyszcząca cache"""
try:
app = TenderEvaluationApp()
return await app.clear_cache()
except Exception as e:
return f"❌ Błąd: {str(e)}"
with gr.Blocks(title="Analiza i ocena ofert przetargowych") as interface:
gr.Markdown("""
# Analiza i ocena ofert przetargowych
Wgraj plik z briefem lub SIWZ oraz pliki z ofertami do oceny.
System przeanalizuje dokumenty i wygeneruje tabelę ocen oraz podsumowanie.
""")
with gr.Row():
with gr.Column(scale=1):
api_key = gr.Textbox(
label="DeepSeek API Key",
type="password",
placeholder="Wprowadź swój klucz API...",
value="" # Pole zawsze puste na starcie
)
brief_input = gr.File(
label="Brief/SIWZ",
file_types=[".pdf", ".docx", ".txt"],
type="filepath"
)
offers_input = gr.File(
label="Oferty",
file_types=[".pptx", ".pdf", ".docx"],
type="filepath",
file_count="multiple"
)
analyze_btn = gr.Button(
"Analizuj dokumenty",
variant="primary"
)
clear_cache_btn = gr.Button(
"Wyczyść cache",
variant="secondary"
)
with gr.Column(scale=2):
with gr.Tab("Wyniki"):
results_df = gr.DataFrame(
label="Oceny",
interactive=False
)
word_output = gr.Markdown()
with gr.Tab("Status"):
status_output = gr.Markdown()
cache_status = gr.Markdown()
analyze_btn.click(
fn=process_with_api_key,
inputs=[api_key, brief_input, offers_input],
outputs=[results_df, word_output, status_output]
)
clear_cache_btn.click(
fn=clear_cache,
inputs=[],
outputs=[cache_status]
)
gr.Markdown("""
### Wspierane formaty
- Brief/SIWZ: PDF, DOCX, TXT
- Oferty: PPTX, PDF, DOCX
### Uwaga
Jeśli nie posiadasz osobistego klucza API do modelu DeepSeek, to możesz go wygenerować tutaj: https://platform.deepseek.com/
""")
return interface
if __name__ == "__main__":
interface = create_interface()
interface.launch(
server_name="0.0.0.0",
server_port=7860,
show_api=False
)