WhizTenderBot1.0 / criteria_analyzer.py
Marek4321's picture
Update criteria_analyzer.py
4a5f7df verified
from typing import List, Dict, Optional
from dataclasses import dataclass
import logging
import hashlib
from cache_manager import CacheManager, cache_result
from deepseek_client import DeepSeekClient
from dataclasses import dataclass, asdict
from typing import Optional
@dataclass
class EvaluationCriterion:
"""Reprezentuje pojedyncze kryterium oceny"""
name: str
weight: float
description: str
scoring_guide: Optional[str] = None
min_score: float = 0.0
max_score: float = 100.0
def to_dict(self) -> dict:
"""Konwertuje obiekt na s艂ownik do serializacji"""
return asdict(self)
@classmethod
def from_dict(cls, data: dict) -> 'EvaluationCriterion':
"""Tworzy obiekt ze s艂ownika"""
return cls(**data)
class CriteriaAnalyzer: # Zmieniono nazw臋 z CachedCriteriaAnalyzer na CriteriaAnalyzer
"""
Analizator kryteri贸w z zintegrowanym systemem cachowania.
"""
def __init__(self, api_key: str, base_url: str, cache_ttl: int = 3600 * 24):
self.logger = logging.getLogger(__name__)
self.client = DeepSeekClient(api_key=api_key, base_url=base_url)
self.cache = CacheManager(
cache_dir="cache/criteria",
ttl=cache_ttl,
max_size_mb=100
)
async def close(self):
"""Zamyka klienta HTTP"""
await self.client.close()
async def __aenter__(self):
"""Context manager entry"""
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit"""
await self.close()
def _compute_brief_hash(self, brief_content: str) -> str:
"""
Generuje unikalny hash dla tre艣ci briefu/SIWZ.
"""
return hashlib.sha256(brief_content.encode()).hexdigest()
@cache_result(ttl=3600 * 24)
async def extract_criteria(self, brief_content: str) -> List[EvaluationCriterion]:
"""
Ekstrahuje kryteria z briefu z wykorzystaniem cache.
Args:
brief_content: Tre艣膰 dokumentu SIWZ/brief
Returns:
List[EvaluationCriterion]: Lista wyodr臋bnionych kryteri贸w
"""
try:
# U偶yj DeepSeek do analizy dokumentu
criteria_data = await self.client.analyze_criteria(brief_content)
# Konwertuj wyniki na obiekty EvaluationCriterion
criteria = [
EvaluationCriterion(
name=c['name'],
weight=float(c['weight']),
description=c['description'],
scoring_guide=c.get('scoring_guide')
)
for c in criteria_data
]
# Upewnij si臋, 偶e suma wag wynosi 100%
self._normalize_weights(criteria)
self.logger.info(f"Wyodr臋bniono {len(criteria)} kryteri贸w")
return criteria
except Exception as e:
self.logger.error(f"B艂膮d podczas ekstrakcji kryteri贸w: {str(e)}")
raise
@cache_result(ttl=3600 * 24)
async def create_evaluation_matrix(
self,
criteria: List[EvaluationCriterion],
offer_ids: List[str]
) -> Dict:
"""
Tworzy matryc臋 ocen z wykorzystaniem cache.
Args:
criteria: Lista kryteri贸w oceny
offer_ids: Lista identyfikator贸w ofert
Returns:
Dict: Matryca ocen
"""
try:
matrix = {
'criteria': [c.name for c in criteria],
'offers': offer_ids,
'weights': [c.weight for c in criteria],
'scores': [[None for _ in offer_ids] for _ in criteria],
'descriptions': [c.description for c in criteria],
'scoring_guides': [c.scoring_guide for c in criteria]
}
self.logger.info(f"Utworzono matryc臋 ocen: {len(criteria)} kryteri贸w x {len(offer_ids)} ofert")
return matrix
except Exception as e:
self.logger.error(f"B艂膮d podczas tworzenia matrycy ocen: {str(e)}")
raise
@cache_result(ttl=3600 * 24)
async def get_criteria_details(
self,
criterion: EvaluationCriterion
) -> Dict[str, str]:
"""
Generuje szczeg贸艂owe wytyczne oceny z wykorzystaniem cache.
Args:
criterion: Kryterium do analizy
Returns:
Dict[str, str]: Szczeg贸艂owe wytyczne oceny
"""
prompt = f"""
Stw贸rz szczeg贸艂owe wytyczne oceny dla kryterium:
Nazwa: {criterion.name}
Waga: {criterion.weight}%
Opis: {criterion.description}
Okre艣l:
1. Co stanowi ocen臋 maksymaln膮 (100%)
2. Co stanowi ocen臋 艣redni膮 (50%)
3. Co stanowi ocen臋 minimaln膮 (0%)
4. Jakie elementy nale偶y wzi膮膰 pod uwag臋
5. Na co szczeg贸lnie zwr贸ci膰 uwag臋
"""
try:
response = await self.client.analyze_text(prompt, "")
return response['choices'][0]['message']['content']
except Exception as e:
self.logger.error(
f"B艂膮d podczas generowania wytycznych dla kryterium {criterion.name}: {str(e)}"
)
return {
"error": f"Nie uda艂o si臋 wygenerowa膰 wytycznych: {str(e)}"
}
def _normalize_weights(self, criteria: List[EvaluationCriterion]) -> None:
"""
Normalizuje wagi kryteri贸w do 100%.
"""
total_weight = sum(c.weight for c in criteria)
if abs(total_weight - 100) > 0.01:
self.logger.warning(
f"Normalizacja wag kryteri贸w (suma pocz膮tkowa: {total_weight}%)"
)
factor = 100 / total_weight
for criterion in criteria:
criterion.weight *= factor
criterion.weight = round(criterion.weight, 2)
total_after = sum(c.weight for c in criteria)
if total_after != 100:
diff = 100 - total_after
criteria[0].weight += diff
async def invalidate_brief_cache(self, brief_content: str):
"""
Invaliduje cache dla konkretnego briefu.
"""
brief_hash = self._compute_brief_hash(brief_content)
await self.cache.invalidate(brief_hash)
async def clear_criteria_cache(self):
"""
Czy艣ci ca艂y cache kryteri贸w.
"""
try:
await self.cache.invalidate_all()
return True
except Exception as e:
self.logger.error(f"B艂膮d podczas czyszczenia cache: {str(e)}")
return False