WhizTenderBot1.0 / report_generator.py
Marek4321's picture
Update report_generator.py
b2ba46b verified
from typing import List, Dict, Optional
import pandas as pd
import os
from pathlib import Path
import logging
from datetime import datetime
import matplotlib.pyplot as plt
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
import xlsxwriter
class ReportGenerator:
"""
Generator raport贸w ko艅cowych z analizy ofert przetargowych.
"""
def __init__(self, output_dir: str = "output"):
self.output_dir = Path(output_dir)
self.output_dir.mkdir(exist_ok=True)
self.logger = logging.getLogger(__name__)
async def generate_excel_report(
self,
analyses: Dict[str, 'OfferAnalysis'],
criteria: List['EvaluationCriterion']
) -> pd.DataFrame:
"""
Generuje raport Excel z wynikami oceny.
Args:
analyses: S艂ownik z analizami ofert
criteria: Lista kryteri贸w oceny
Returns:
pd.DataFrame: DataFrame z wynikami
"""
try:
# Przygotuj dane do DataFrame
data = []
for criterion in criteria:
row = {
'Kryterium': criterion.name,
'Waga': f"{criterion.weight}%"
}
# Dodaj oceny dla ka偶dej oferty
for offer_id, analysis in analyses.items():
eval = next(e for e in analysis.evaluations
if e.criterion_name == criterion.name)
row[f"{offer_id}_score"] = eval.score
row[f"{offer_id}_justification"] = eval.justification
data.append(row)
# Utw贸rz DataFrame
df = pd.DataFrame(data)
# Zapisz do Excel z formatowaniem
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
excel_path = self.output_dir / f"wyniki_oceny_{timestamp}.xlsx"
with pd.ExcelWriter(excel_path, engine='xlsxwriter') as writer:
df.to_excel(writer, sheet_name='Oceny', index=False)
# Dodaj formatowanie
workbook = writer.book
worksheet = writer.sheets['Oceny']
# Formaty
header_format = workbook.add_format({
'bold': True,
'bg_color': '#4F81BD',
'font_color': 'white'
})
score_format = workbook.add_format({
'num_format': '0.00',
'align': 'center'
})
# Zastosuj formaty
for col_num, value in enumerate(df.columns.values):
worksheet.write(0, col_num, value, header_format)
# Dostosuj szeroko艣ci kolumn
worksheet.set_column('A:A', 30) # Kryterium
worksheet.set_column('B:B', 10) # Waga
for i in range(2, len(df.columns), 2):
worksheet.set_column(i, i, 15) # Ocena
worksheet.set_column(i+1, i+1, 50) # Uzasadnienie
self.logger.info(f"Wygenerowano raport Excel: {excel_path}")
return df
except Exception as e:
self.logger.error(f"B艂膮d podczas generowania raportu Excel: {str(e)}")
raise
async def generate_word_report(
self,
analyses: Dict[str, 'OfferAnalysis'],
criteria: List['EvaluationCriterion'],
comparison_results: Dict
) -> str:
"""
Generuje szczeg贸艂owy raport Word z analiz膮 ofert.
Args:
analyses: S艂ownik z analizami ofert
criteria: Lista kryteri贸w oceny
comparison_results: Wyniki por贸wnania ofert
Returns:
str: Markdown z raportem
"""
try:
# Generuj raport w formacie Markdown
report_lines = []
# 1. Nag艂贸wek
report_lines.append("# Raport z Oceny Ofert Przetargowych")
report_lines.append(f"Data utworzenia: {datetime.now().strftime('%d.%m.%Y')}")
report_lines.append(f"Liczba ocenionych ofert: {len(analyses)}")
# 2. Podsumowanie wykonawcze
report_lines.append("\n## Podsumowanie wykonawcze")
# Tabela z kluczowymi wynikami
report_lines.append("\n| Oferent | Wynik ko艅cowy | Ranking |")
report_lines.append("|----------|---------------|---------|")
# Posortuj oferty wg wyniku
sorted_offers = sorted(
analyses.items(),
key=lambda x: x[1].total_score,
reverse=True
)
for i, (offer_id, analysis) in enumerate(sorted_offers, 1):
report_lines.append(
f"| {offer_id} | {analysis.total_score:.2f} | {i} |"
)
# 3. Szczeg贸艂owa analiza ofert
for offer_id, analysis in analyses.items():
report_lines.append(f"\n## Analiza oferty: {offer_id}")
report_lines.append(f"Wynik ko艅cowy: {analysis.total_score:.2f}/100 punkt贸w")
report_lines.append("\n### Mocne strony:")
for strength in analysis.strengths:
report_lines.append(f"* {strength}")
report_lines.append("\n### S艂abe strony:")
for weakness in analysis.weaknesses:
report_lines.append(f"* {weakness}")
report_lines.append("\n### Szczeg贸艂owe oceny:")
report_lines.append("\n| Kryterium | Ocena | Uzasadnienie |")
report_lines.append("|-----------|--------|--------------|")
for eval in analysis.evaluations:
report_lines.append(
f"| {eval.criterion_name} | {eval.score:.2f} | {eval.justification} |"
)
# 4. Por贸wnanie ofert
report_lines.append("\n## Por贸wnanie ofert")
report_lines.append(str(comparison_results))
report = "\n".join(report_lines)
self.logger.info("Wygenerowano raport Word w formacie Markdown")
return report
except Exception as e:
self.logger.error(f"B艂膮d podczas generowania raportu Word: {str(e)}")
raise
def _generate_bar_plot(self, analyses: Dict[str, 'OfferAnalysis']) -> str:
"""
Generuje wykres s艂upkowy por贸wnuj膮cy wyniki ko艅cowe.
Args:
analyses: S艂ownik z analizami ofert
Returns:
str: 艢cie偶ka do zapisanego wykresu
"""
try:
# Przygotuj dane
offer_ids = list(analyses.keys())
scores = [analysis.total_score for analysis in analyses.values()]
# Utw贸rz wykres
plt.figure(figsize=(10, 6))
plt.bar(offer_ids, scores)
# Dodaj etykiety
plt.title('Por贸wnanie wynik贸w ko艅cowych')
plt.xlabel('Oferenci')
plt.ylabel('Wynik ko艅cowy')
plt.xticks(rotation=45)
# Dodaj warto艣ci nad s艂upkami
for i, score in enumerate(scores):
plt.text(i, score, f'{score:.1f}', ha='center', va='bottom')
# Zapisz wykres
plot_path = self.output_dir / 'wyniki_koncowe.png'
plt.savefig(plot_path, bbox_inches='tight')
plt.close()
return str(plot_path)
except Exception as e:
self.logger.error(f"B艂膮d podczas generowania wykresu: {str(e)}")
return ""