/
nevers
/
hackINTERPOL
Обзор
Документация
Войти
/
nevers
/
hackINTERPOL
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/studypulse/report.py
277 строк
8 KB
nevers
upload files
13 янв 2026, 17:14
13 янв 2026, 17:14
e86a850
Код
Авторство
О чём код?
"""HTML report generation for StudyPulse.""" import base64 import io from pathlib import Path import matplotlib import matplotlib.pyplot as plt import pandas as pd from studypulse.metrics import compute_descriptive_stats, compute_risk_score, get_top_risks from studypulse.model import train_and_predict from studypulse.utils import set_deterministic_seed # Use non-interactive backend matplotlib.use("Agg") set_deterministic_seed(42) def plot_to_base64(fig: plt.Figure) -> str: """Convert matplotlib figure to base64 encoded PNG string. Args: fig: Matplotlib figure object. Returns: Base64 encoded PNG string. """ buf = io.BytesIO() fig.savefig(buf, format="png", dpi=100, bbox_inches="tight") buf.seek(0) img_base64 = base64.b64encode(buf.read()).decode("utf-8") buf.close() plt.close(fig) return img_base64 def create_score_histogram(df: pd.DataFrame) -> str: """Create histogram of assignment scores. Args: df: DataFrame with assignment_score column. Returns: Base64 encoded PNG string. """ fig, ax = plt.subplots(figsize=(8, 5)) ax.hist(df["assignment_score"], bins=20, edgecolor="black", alpha=0.7) ax.set_xlabel("Assignment Score") ax.set_ylabel("Number of Students") ax.set_title("Distribution of Assignment Scores") ax.grid(True, alpha=0.3) return plot_to_base64(fig) def create_attendance_scatter(df: pd.DataFrame) -> str: """Create scatter plot of attendance rate vs assignment score. Args: df: DataFrame with attendance_rate and assignment_score columns. Returns: Base64 encoded PNG string. """ fig, ax = plt.subplots(figsize=(8, 5)) ax.scatter( df["attendance_rate"], df["assignment_score"], alpha=0.6, s=50, edgecolors="black", linewidth=0.5, ) ax.set_xlabel("Attendance Rate") ax.set_ylabel("Assignment Score") ax.set_title("Attendance Rate vs Assignment Score") ax.grid(True, alpha=0.3) return plot_to_base64(fig) def dataframe_to_html_table(df: pd.DataFrame, table_id: str = "") -> str: """Convert DataFrame to HTML table. Args: df: DataFrame to convert. table_id: Optional ID for the table element. Returns: HTML table string. """ html = df.to_html( index=False, classes="data-table", table_id=table_id, escape=False, float_format=lambda x: f"{x:.2f}" if isinstance(x, float) else str(x), ) return html def generate_html_report(df: pd.DataFrame, output_path: str | Path) -> None: """Generate a self-contained HTML report. Args: df: DataFrame with student data. output_path: Path to write the HTML report. """ # Compute metrics stats = compute_descriptive_stats(df) df_with_risk = compute_risk_score(df) top_risks = get_top_risks(df_with_risk, top_n=15) # Train model and get predictions (used for future model integration) train_and_predict(df) # Generate plots score_hist_img = create_score_histogram(df) attendance_scatter_img = create_attendance_scatter(df) # Build HTML html_content = f"""<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>StudyPulse Learning Analytics Report</title> <style> body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; }} h1 {{ color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }} h2 {{ color: #34495e; margin-top: 30px; border-bottom: 2px solid #ecf0f1; padding-bottom: 5px; }} .summary-stats {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; }} .stat-card {{ background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }} .stat-value {{ font-size: 2em; font-weight: bold; color: #3498db; }} .stat-label {{ color: #7f8c8d; font-size: 0.9em; }} .data-table {{ width: 100%; border-collapse: collapse; margin: 20px 0; background: white; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }} .data-table th {{ background-color: #3498db; color: white; padding: 12px; text-align: left; font-weight: 600; }} .data-table td {{ padding: 10px; border-bottom: 1px solid #ecf0f1; }} .data-table tr:hover {{ background-color: #f8f9fa; }} .plot-container {{ background: white; padding: 20px; margin: 20px 0; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); text-align: center; }} .plot-container img {{ max-width: 100%; height: auto; }} .risk-high {{ color: #e74c3c; font-weight: bold; }} .risk-medium {{ color: #f39c12; }} .risk-low {{ color: #27ae60; }} </style> </head> <body> <h1>📊 StudyPulse Learning Analytics Report</h1> <h2>Summary Statistics</h2> <div class="summary-stats"> <div class="stat-card"> <div class="stat-value">{stats['total_students']}</div> <div class="stat-label">Total Students</div> </div> <div class="stat-card"> <div class="stat-value">{stats['mean_assignment_score']:.1f}</div> <div class="stat-label">Mean Assignment Score</div> </div> <div class="stat-card"> <div class="stat-value">{stats['mean_attendance_rate']:.1%}</div> <div class="stat-label">Mean Attendance Rate</div> </div> <div class="stat-card"> <div class="stat-value">{stats['low_score_count']}</div> <div class="stat-label">Students with Low Scores (<60)</div> </div> <div class="stat-card"> <div class="stat-value">{stats['low_attendance_count']}</div> <div class="stat-label">Students with Low Attendance (<70%)</div> </div> <div class="stat-card"> <div class="stat-value">{stats['inactive_count']}</div> <div class="stat-label">Inactive Students (14+ days)</div> </div> </div> <h2>Top Risk Students</h2> {dataframe_to_html_table(top_risks, 'top-risks')} <h2>Score Distribution</h2> <div class="plot-container"> <img src="data:image/png;base64,{score_hist_img}" alt="Score Distribution Histogram"> </div> <h2>Attendance vs Score Analysis</h2> <div class="plot-container"> <img src="data:image/png;base64,{attendance_scatter_img}" alt="Attendance vs Score Scatter Plot"> </div> <h2>Detailed Statistics</h2> <div class="stat-card"> <p><strong>Median Assignment Score:</strong> {stats['median_assignment_score']:.2f}</p> <p><strong>Standard Deviation (Scores):</strong> {stats['std_assignment_score']:.2f}</p> <p><strong>Mean Assignments Submitted:</strong> {stats['mean_assignments_submitted']:.2f}</p> <p><strong>Mean Days Since Last Submission:</strong> {stats['mean_days_since_last_submission']:.2f}</p> <p><strong>Students Behind Deadline:</strong> {stats['behind_deadline_count']}</p> </div> <footer style="margin-top: 40px; padding-top: 20px; border-top: 2px solid #ecf0f1; color: #7f8c8d; text-align: center;"> <p>Generated by StudyPulse - Learning Analytics CLI</p> <p>Report contains {len(df)} student records</p> </footer> </body> </html>""" # Write to file output_path = Path(output_path) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(html_content, encoding="utf-8")