Spaces:
Running
Running
import os | |
from typing import Dict, List, Optional, NamedTuple, Tuple | |
from datetime import datetime | |
from datasets import load_dataset | |
from huggingface_hub import HfApi | |
# Environment variables and constants | |
EXAM_DATASET_ID = os.getenv( | |
"EXAM_DATASET_ID", "agents-course/unit_1_quiz_student_responses" | |
) | |
CERTIFICATE_MODELS_DIR = os.getenv("CERTIFICATE_MODELS_DIR", "./certificate_models") | |
CERTIFICATE_PATH = os.path.join(CERTIFICATE_MODELS_DIR, "certificate.png") | |
PASSING_THRESHOLD = float(os.getenv("PASSING_THRESHOLD", "0.8")) # 80% | |
MIN_QUESTIONS = int(os.getenv("MIN_QUESTIONS", "1")) | |
class CertificateResult(NamedTuple): | |
"""Stores the result of a certificate check""" | |
message: str | |
certificate_path: Optional[str] | |
pass_percentage: float | |
passed: bool | |
results_df: Optional[object] = None | |
def get_user_results(username: str) -> List[Dict]: | |
""" | |
Get user's quiz results from the dataset. | |
Args: | |
username: The Hugging Face username to check | |
Returns: | |
List of user's quiz results | |
""" | |
try: | |
user_results = load_dataset(path=EXAM_DATASET_ID, split=username) | |
results = user_results.to_list() | |
print(f"Found {len(results)} results for user {username}") | |
return results | |
except Exception as e: | |
print(f"Error in get_user_results: {str(e)}") | |
raise | |
def calculate_pass_percentage(results: List[Dict]) -> Tuple[float, int]: | |
""" | |
Calculate the user's pass percentage and number of questions from their results. | |
The dataset structure has: | |
- is_correct: bool indicating if answer was correct | |
- grade: float64 indicating overall grade | |
- datetime: string of attempt timestamp | |
Args: | |
results: List of quiz results | |
Returns: | |
Tuple of (highest grade achieved, number of questions answered) | |
""" | |
try: | |
if not results: | |
return 0.0, 0 | |
# Group results by datetime to get distinct attempts | |
attempts = {} | |
for result in results: | |
timestamp = result["datetime"] | |
if timestamp not in attempts: | |
attempts[timestamp] = { | |
"correct": 0, | |
"total": 0, | |
"grade": result.get("grade", 0.0), | |
} | |
attempts[timestamp]["total"] += 1 | |
if result["is_correct"]: | |
attempts[timestamp]["correct"] += 1 | |
# Find the best attempt | |
best_attempt = max( | |
attempts.values(), | |
key=lambda x: x["grade"] | |
if x["grade"] is not None | |
else (x["correct"] / x["total"] if x["total"] > 0 else 0), | |
) | |
# If grade is available, use it; otherwise calculate from correct/total | |
if best_attempt["grade"] is not None and best_attempt["grade"] > 0: | |
pass_percentage = float(best_attempt["grade"]) | |
else: | |
pass_percentage = ( | |
best_attempt["correct"] / best_attempt["total"] | |
if best_attempt["total"] > 0 | |
else 0.0 | |
) | |
return pass_percentage, best_attempt["total"] | |
except Exception as e: | |
print(f"Error in calculate_pass_percentage: {str(e)}") | |
raise | |
def has_passed(pass_percentage: float, num_questions: int) -> bool: | |
""" | |
Check if user has passed based on percentage and minimum questions. | |
Args: | |
pass_percentage: User's highest quiz score | |
num_questions: Number of questions answered | |
Returns: | |
Boolean indicating if user passed | |
""" | |
return pass_percentage >= PASSING_THRESHOLD and num_questions >= MIN_QUESTIONS | |
def get_certificate_result( | |
pass_percentage: float, num_questions: int | |
) -> CertificateResult: | |
""" | |
Determine if user passed and create appropriate message. | |
Args: | |
pass_percentage: User's highest quiz score | |
num_questions: Number of questions answered | |
Returns: | |
CertificateResult with pass status and details | |
""" | |
passed = has_passed(pass_percentage, num_questions) | |
if passed: | |
message = """ | |
Congratulations, you successfully completed the course! π \n | |
You can download your certificate below β¬οΈ | |
""" | |
return CertificateResult( | |
message=message, | |
certificate_path=CERTIFICATE_PATH, | |
pass_percentage=pass_percentage, | |
passed=True, | |
) | |
else: | |
return CertificateResult( | |
message=""" | |
You haven't completed all the requirements yet. \n | |
Keep trying! πͺ | |
""", | |
certificate_path=None, | |
pass_percentage=pass_percentage, | |
passed=False, | |
) | |
def check_certification(username: str) -> CertificateResult: | |
""" | |
Check if a user has completed the certification requirements. | |
Args: | |
username: The Hugging Face username to check | |
Returns: | |
CertificateResult containing pass status and details | |
""" | |
try: | |
# Get user's quiz results | |
results = get_user_results(username) | |
if not results: | |
return CertificateResult( | |
message="No quiz results found. Please complete the quiz first.", | |
certificate_path=None, | |
pass_percentage=0.0, | |
passed=False, | |
) | |
# Calculate pass percentage and get appropriate certificate result | |
pass_percentage, num_questions = calculate_pass_percentage(results) | |
return get_certificate_result(pass_percentage, num_questions) | |
except Exception as e: | |
error_msg = """ | |
There was an error checking your certification status. | |
Please try again later or contact support if the issue persists. | |
""" | |
return CertificateResult( | |
message=error_msg, | |
certificate_path=None, | |
pass_percentage=0.0, | |
passed=False, | |
) | |